fix(codegen): in-memory (Dynamic) handler codegen in Production#104
Merged
Conversation
The published container ran Wolverine codegen with TypeLoadMode.Auto. On the
first hit of every handler it generated the code dynamically (Roslyn) and then
tried to persist the source to /app/Internal — which the non-root `app` user
cannot write — logging "Access to the path '/app/Internal' is denied". The write
is non-fatal but noisy, and Auto's persisted source is never recompiled into the
running image, so it bought nothing in a container.
Switch Production to TypeLoadMode.Dynamic: handlers are compiled in memory and
never written to disk, so the error and the failed write are gone. Dev and the
Testing-env integration suite keep Auto (writable working tree), so they are
unchanged.
Note on the road not taken: pre-generating at build time + TypeLoadMode.Static
would also remove the per-handler cold-compile, but it requires JasperFx's
`RunJasperFxCommands` entry point. Any reference to that API in this assembly
wires JasperFx into the host resolution that WebApplicationFactory drives, which
tears the integration-test host down before it initializes ("Server hasn't been
initialized yet"); the official `JasperFxEnvironment.AutoStartHost` workaround in
turn starts hosted services before the imperative multi-tenant bootstrap has
provisioned the `system` tenant. Static is therefore deferred until the startup
bootstrap can be reworked into ordered hosted services.
Verified: full backend suite green — 1254 unit + 444 integration tests pass
(the integration classes that this approach's earlier Static attempt broke —
FlavorRegistryTests, FirstSignalConsistencyFlowTests, AuthHardeningWave2Tests —
included).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3af10cf to
b7ad11b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The deployed container logged this on the first hit of every handler:
Wolverine ran codegen with
TypeLoadMode.Auto. At runtime it generated each handler dynamically (Roslyn) and then tried to persist the source to/app/Internal— which the non-rootappuser cannot write (only/app/datais chowned). The write is non-fatal but noisy, and Auto's persisted source is never recompiled into the running image, so it buys nothing in a container.Fix
Switch Production to
TypeLoadMode.Dynamic: handlers are compiled in memory and never written to disk, so the error and the failed write disappear. Dev and theTesting-env integration suite keepAuto(writable working tree) and are unchanged.One line,
IsProduction()-gated. No Dockerfile or test-harness changes.Road not taken — pre-gen +
StaticThe first attempt on this branch pre-generated the handler code in the Docker build and ran
TypeLoadMode.Staticto also eliminate the per-handler cold-compile. That requires JasperFx'sRunJasperFxCommandsentry point — and any reference to that API in this assembly wires JasperFx into the host resolution thatWebApplicationFactorydrives viaHostFactoryResolver, which tore the integration-test host down before it initialized (Server hasn't been initialized yet). The officialJasperFxEnvironment.AutoStartHost = trueworkaround in turn starts hosted services before the imperative multi-tenant bootstrap has provisioned thesystemtenant (UnknownTenantIdException: 'system').So
Staticis deferred until the startup bootstrap can be reworked into ordered hosted services.Dynamicfixes the actual reported problem now; the only thing it doesn't recover is the cold-compile on the first hit after a restart (which the container pays today anyway).Verification
Staticattempt broke (FlavorRegistryTests,FirstSignalConsistencyFlowTests,AuthHardeningWave2Tests) pass.🤖 Generated with Claude Code