diff --git a/packages/sequencer/src/sequencer/executor/Sequencer.ts b/packages/sequencer/src/sequencer/executor/Sequencer.ts index 4a37037c..7089f988 100644 --- a/packages/sequencer/src/sequencer/executor/Sequencer.ts +++ b/packages/sequencer/src/sequencer/executor/Sequencer.ts @@ -66,6 +66,14 @@ export class Sequencer * modules to start each */ public async start() { + // The sequencer uses tsyringe to resolve modules (and their dependencies) + // and then starts them. However, this can be problematic as although tsyringe may resolve + // dependencies, it doesn't actually start them. For example, a database may be created, + // but the connection strings, etc, won't be constructed until it's started, and this may + // cause an error if a module that relies on it is started first. The way to fix this is + // ensure that we start modules based on the order they were resolved. + // We iterate through the methods three times: + this.useDependencyFactory(this.container.resolve(MethodIdFactory)); // Log startup info @@ -75,15 +83,41 @@ export class Sequencer log.info("Starting sequencer..."); log.info("Modules:", moduleClassNames); + // Iteration #1: We invoke the afterResolution feature for the container + // to ensure every time a module is resolved it gets recorded. + const orderedModules: Extract[] = []; + // eslint-disable-next-line guard-for-in + for (const moduleName in this.definition.modules) { + this.container.afterResolution( + moduleName, + () => { + orderedModules.push(moduleName); + }, + { + frequency: "Once", + } + ); + } + // Iteration #2: We resolve each module and thus populate + // the orderedModules list to understand the sequencing. // eslint-disable-next-line guard-for-in for (const moduleName in this.definition.modules) { const sequencerModule = this.resolve(moduleName); - log.info( - `Starting sequencer module ${moduleName} (${sequencerModule.constructor.name})` + `Resolving sequencer module ${moduleName} (${sequencerModule.constructor.name})` ); + } + + // Iteration #3: We now iterate though the orderedModules list + // and start the modules in the order they were resolved. + for (const moduleName of orderedModules) { + const sequencerModule = this.resolve(moduleName); // eslint-disable-next-line no-await-in-loop await sequencerModule.start(); + + log.info( + `Starting sequencer module ${moduleName} (${sequencerModule.constructor.name})` + ); } } }