fix: Resume workflow execution (#7103)

Supercedes #7051 – if OK, I'll move the base of this PR to `develop` and we can run reviews only of this one.

**What**
- Gracefully close BullMQ Worker in Redis Event Bus and Redis Workflow Engine (by @edast, @sradevski)
- Register workflows before MedusaApp is loaded*
- Introduce `onApplicationPrepareShutdown`**
- Refactor plugin resolving for reusability purposes

*We now register workflows before modules are loaded to ensure modules can run workflows as part of bootstrapping. E.g. the Redis Workflow Engine resumes workflows when it starts, which has until this change failed, because the workflows were not registered yet.

**We introduce a new hook to prepare resources for an application shutdown. E.g. closing the BullMQ worker as a preparatory step to closing the BullMQ queue. The worker will continue to process jobs while the queue is still open to receive new jobs (without processing them). 

Co-authored-by: Stevche Radevski <4820812+sradevski@users.noreply.github.com>
Co-authored-by: Darius <618221+edast@users.noreply.github.com>
Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
This commit is contained in:
Oli Juhl
2024-04-22 14:32:50 +02:00
committed by GitHub
parent 67a21c3e45
commit 0eb68541b8
24 changed files with 351 additions and 228 deletions
@@ -141,6 +141,7 @@ export async function loadInternalModule(
if (loaderOnly) {
// The expectation is only to run the loader as standalone, so we do not need to register the service and we need to cleanup all services
const service = container.resolve(registrationName)
await service.__hooks?.onApplicationPrepareShutdown()
await service.__hooks?.onApplicationShutdown()
}
}
+8
View File
@@ -204,6 +204,7 @@ export type MedusaAppOutput = {
notFound?: Record<string, Record<string, string>>
runMigrations: RunMigrationFn
onApplicationShutdown: () => Promise<void>
onApplicationPrepareShutdown: () => Promise<void>
}
export type MedusaAppOptions = {
@@ -243,6 +244,7 @@ async function MedusaApp_({
migrationOnly?: boolean
} = {}): Promise<MedusaAppOutput> {
const sharedContainer_ = createMedusaContainer({}, sharedContainer)
const onApplicationShutdown = async () => {
await promiseAll([
MedusaModule.onApplicationShutdown(),
@@ -250,6 +252,10 @@ async function MedusaApp_({
])
}
const onApplicationPrepareShutdown = async () => {
await promiseAll([MedusaModule.onApplicationPrepareShutdown()])
}
const modules: MedusaModuleConfig =
modulesConfig ??
(
@@ -312,6 +318,7 @@ async function MedusaApp_({
if (loaderOnly) {
return {
onApplicationShutdown,
onApplicationPrepareShutdown,
modules: allModules,
link: undefined,
query: async () => {
@@ -388,6 +395,7 @@ async function MedusaApp_({
return {
onApplicationShutdown,
onApplicationPrepareShutdown,
modules: allModules,
link: remoteLink,
query,
+17
View File
@@ -136,6 +136,23 @@ export class MedusaModule {
)
}
public static async onApplicationPrepareShutdown(): Promise<void> {
await promiseAll(
[...MedusaModule.instances_.values()]
.map((instances) => {
return Object.values(instances).map((instance: IModuleService) => {
return instance.__hooks?.onApplicationPrepareShutdown
?.bind(instance)()
.catch(() => {
// The module should handle this and log it
return void 0
})
})
})
.flat()
)
}
public static clearInstances(): void {
MedusaModule.instances_.clear()
MedusaModule.modules_.clear()