Repository navigation
fix(core): run the shutdown sequence after a failed init - #17966
Merged
Merged
Conversation
`close()` and the shutdown signal handlers awaited the initialization promise as it was, so after a failed `init()` they rethrew the init error and skipped the whole teardown: `onModuleDestroy`, `beforeApplicationShutdown`, `dispose()` and `onApplicationShutdown` never ran, and what the partial startup had acquired (connection pools, timers, sockets) leaked. A signal also logged the init error as a shutdown error and exited with code 1. A shutdown still waits for an initialization in flight to settle, but no longer adopts its failure. Application contexts and testing modules now behave like HTTP applications, whose `init()` never set that promise.
4 of 12 tasks
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.
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
When
init()of an application context fails, closing it afterwards tears nothing down.close()rejects with the init error, and noonModuleDestroy,beforeApplicationShutdownoronApplicationShutdownhook runs. Whatever the partial startup acquired stays open: a connection pool a module created before another provider'sonModuleInit()threw, timers, sockets. A shutdown signal does the same: it logs the init error as "Error happened during shutdown" and exits with code 1, without running a hook.The cause is in
runShutdownSequence(). Since the change that made a shutdown wait for an initialization in flight, it starts withawait this.initializationPromise. After a failedinit(), that promise is rejected, so the shutdown rethrows the init error before its first step.NestApplicationdoesn't have the problem, because its owninit()never sets that promise. So a failedapp.init()followed byapp.close()already tears down. Standalone application contexts (NestFactory.createApplicationContext()) andTestingModule#init()do have it.What is the new behavior?
A shutdown still waits for an initialization in flight to settle, but it no longer adopts its failure. The init error has already reached the caller of
init().close()and the signal handlers then run the whole teardown and release what the startup acquired, as they do for HTTP applications. A signal that arrives while an initialization that later fails is still running waits for it, then tears down.Tests:
NestApplicationContext:close()after a failedinit()runs the destroy and both shutdown hooks;init()runs the shutdown hooks and re-raises the signal, instead of exiting with 1;init()that later fails waits for it to settle, then runs the hooks.TestingModulewhoseinit()fails callsonApplicationShutdownonclose().Each new test fails without the fix.
Does this PR introduce a breaking change?
Other information
close()after a failedinit()now resolves if the teardown succeeds, instead of rejecting with the init error again. Code that awaitedclose()to rethrow the init error would get it frominit()already.