shashank created CAMEL-25606:
--------------------------------

             Summary: camel-wasm - Optional runtime compiler for the wasm 
endpoint (compile=true, module loaded and compiled once per endpoint)
                 Key: CAMEL-25606
                 URL: https://issues.apache.org/jira/browse/CAMEL-25606
             Project: Camel
          Issue Type: Improvement
            Reporter: shashank
            Assignee: shashank


Since CAMEL-25531 the {{wasm}} endpoint runs WASI Preview 1 command modules, 
one new instance per exchange, with the Endive interpreter. The interpreter is 
slow for modules that compute a lot per message or have a large runtime 
(standard Go programs). Endive has a runtime compiler that translates a module 
to JVM bytecode. This ticket makes it available as an endpoint option.

h3. The option

* {{compile}} (boolean, endpoint option, default {{false}}). With 
{{compile=true}} the module is translated to JVM bytecode with 
{{run.endive.compiler.MachineFactoryCompiler}} instead of being interpreted.
* Default off, because compiling costs start-up time and generates classes at 
run time, which a GraalVM native image cannot load.
* Everything else is the same with the compiler on: result headers, exit codes, 
traps, {{maxMemoryPages}}, {{maxOutputSize}}, environment and arguments, 
{{SecureRandom}} for {{random_get}}, {{timeout}} (compiled code checks the 
thread interrupt like the interpreter), the body read on the calling thread 
when a timeout is set, streams opened by the type converter closed, the body 
after a failed run. The option gives a module no new capability.
* New dependency of camel-wasm: {{run.endive:compiler}} (Apache-2.0, 163 KB), 
which brings ASM in the version Camel already manages.
* New public API: {{WasmConfiguration.isCompile/setCompile}}, 
{{WasiCommand(WasmModule, List<String>, int, boolean)}}, 
{{WasiCommand.isCompiled()}}. The three-argument constructor is kept.

h3. When the module is loaded and compiled

* Once per endpoint, when the first producer of the endpoint is initialized: 
when the route is first started, or at the first message with 
{{lazyStartProducer=true}} or {{toD}}. {{WasmEndpoint}} keeps one immutable 
{{WasiCommand}} for as long as it exists, and all its producers share it. 
Exchanges still run on separate instances; they share only the generated 
classes.
* With CAMEL-25531 every producer parsed the module itself, and a route start 
creates a new producer, so a compiler in the producer would compile the same 
module at every route start. With this change a route stop and start neither 
reads nor compiles the module again. A changed module file is read by a new 
endpoint only (route removed and added again, application restarted).
* The generated classes can be unloaded when the endpoint is no longer 
reachable, not when a route stops. A route reload in development mode removes 
all routes and endpoints by default, so it reads and compiles the module again.
* With a dynamic endpoint ({{toD}}) every different URI is an endpoint with its 
own compiled copy of the module, so many different compiled URIs make metaspace 
grow. The component page says so.
* A failed load is not kept: the next producer that is initialized tries again.

h3. Failure modes

* A function that is too large for a JVM method is left to the interpreter and 
the rest of the module is compiled. Endive reports that on {{System.err}} by 
default ({{InterpreterFallback.WARN}}). The component compiles with {{FAIL}} 
first and, when that fails, again with {{SILENT}}, and logs once per endpoint 
at WARN with the name of the module; nothing is written to {{System.err}}.
* A module the compiler cannot translate fails when the first producer is 
initialized, with an {{IllegalStateException}} that names the module by its 
file name and has the Endive exception as cause.
* {{WasiCommand}} refers to the compiler through one nested class, so it still 
loads and interprets when {{run.endive:compiler}} is excluded from the 
classpath; {{compile=true}} then fails with an {{IllegalStateException}} that 
names the artifact.

h3. Docs

* Component page, section Performance: the option, when the module is compiled, 
when to keep the default. No numbers.
* Upgrade guide 4.24: one paragraph in the existing camel-wasm section (the 
option, the new dependency, the module loaded once per endpoint).

h3. Tests

* camel-wasm: 132 tests, 0 failures. {{WasmCompiledComponentTest}} extends 
{{WasmComponentTest}} and runs all its 58 tests with {{compile=true}}; no test 
behaves differently with the compiler on.
* New tests: the second producer of an endpoint gets the same command without 
reading the module again; a route stop and start keeps the command; a load that 
failed is tried again by the next producer; a trap shows generated classes with 
{{compile=true}} and interpreter frames without; a function that is too large 
is interpreted, the module runs and nothing is printed to {{System.err}}; a 
module that cannot be compiled fails with its name; {{compile=true}} without 
the compiler on the classpath fails with the name of the artifact.

h3. Not run

* A GraalVM native image (neither that {{compile=true}} fails there nor that 
the interpreter works there).
* The full build from the root.
* A route reload in development mode with a compiled module, and a metaspace 
measurement with {{toD}}.
* Performance on the final change: earlier measurements through a route (one 
machine, an earlier state of the CAMEL-25531 work) showed compiled modules 5 to 
150 times faster per exchange and a first start of up to about a second for a 
2.3 MB Go program; they are indicative only and are in the pull request.

h3. Open questions for the pull request

* {{run.endive:compiler}} as a normal dependency (proposed, about 0.55 MB with 
ASM) or an optional one (which needs {{--dep}} with {{camel run}}).
* A hook for modules compiled at build time with the Endive Maven plugin (the 
native-image friendly way); not part of this ticket.
* The handling of functions that are too large: fallback to the interpreter 
with a WARN (proposed), silent fallback, or failure.
* The module is now loaded by the endpoint and kept as long as the endpoint 
exists.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to