Repository navigation
feat: add a warmup phase to the application lifecycle - #48
Merged
Merged
Conversation
Adds "app.warmUp" to assemble the application without ever making it ready. The method starts the providers, runs the "starting" hooks and imports the preload files, which is everything "app.start" used to do before invoking its callback. Now "app.start" delegates to it, so nothing moves for an app that runs. The application is also given a mode. The default "run" mode behaves exactly like before, whereas the "warmup" mode tells the providers that the app is only being assembled to be inspected, hence they must skip their long running side-effects. Calling "app.start" on such an app throws and terminating it skips the providers shutdown hooks, since nothing was ever opened. The phase exists for the tooling that has to inspect an application instead of using it. For example, generating the route types without starting the HTTP server.
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.
Summary
Adds a warmup phase to the application lifecycle, so an application can be fully assembled without ever being made ready.
This is groundwork for adonisjs/assembler#100 and the
node ace codegencommand in core, which need a booted application to read the routes from, but must not start an HTTP server, queue workers or a Vite dev server along the way.What changed
app.warmUp()starts the providers, runs thestartinghooks and imports the preload files. That is everythingapp.startused to do before invoking its callback, soapp.startnow simply delegates to it. The order is unchanged, which matters because preload files rely on the state established byproviders.start()—@adonisjs/queueinitialises its manager there and thestart/scheduler.tspreload schedules jobs against it.Application modes. An app is created with a mode, defaulting to
run.run— behaves exactly as before.warmup— the app is only being assembled to be inspected.app.startthrows, andapp.terminateskips the providers shutdown hooks.Providers use the mode to skip their long running side-effects:
The mode must never be used to register different bindings. The app being inspected has to match the app that runs, otherwise the generated code describes something that does not exist.
Skipping shutdown in warmup mode. Every shutdown hook begins by resolving its manager from the container (
container.make('lucid.db'),container.make('redis')and so on). Sinceregister()only binds lazy factories, running them for a warmed up app would construct the very resources that were never used, just to tear them down. Terminating already skips provider shutdown for shallower states, because#providersWithShutdownListenersis populated duringregister().Is this breaking?
No, it is a minor.
Additive:
warmUp(),getMode(),setMode(),isWarmedUp,ApplicationModesand the optionalmodeconstructor option. Mode defaults torun, so the newstartthrow and the shutdown skip are unreachable for existing code.Two things are technically observable, neither in the documented surface:
ApplicationStatesgains'warming' | 'warmed', so an exhaustiveswitchovergetState()loses exhaustiveness understrict.start()the state now passes through'warming'/'warmed'instead of staying'booted', which is visible to code callinggetState()from inside astartinghook or a preload.Tests
11 new tests in
tests/application/warm_up.spec.tscovering warm-then-stop,providers.start()running whileready()does not, preload import during warmup, idempotency, thestartrejection, terminate from'warmed', hook ordering for a normal run, starting an already warmed app, and the mode accessors.