diff --git a/.changeset/search-heavy-frogs-confess.md b/.changeset/search-heavy-frogs-confess.md new file mode 100644 index 0000000000..a37c28c60c --- /dev/null +++ b/.changeset/search-heavy-frogs-confess.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-node': patch +--- + +Fixed a bug that could cause a `max listeners exceeded warning` to be logged when more than 10 collators were running simultaneously. diff --git a/plugins/search-backend-node/src/Scheduler.test.ts b/plugins/search-backend-node/src/Scheduler.test.ts index 30e5c7e416..6daae3013d 100644 --- a/plugins/search-backend-node/src/Scheduler.test.ts +++ b/plugins/search-backend-node/src/Scheduler.test.ts @@ -204,4 +204,40 @@ describe('Scheduler', () => { ); }); }); + + describe('stop', () => { + it('should abort tasks on stop', () => { + const run = jest.fn(); + + // Add tasks and interval to schedule + testScheduler.addToSchedule({ + id: '1', + task: jest.fn(), + scheduledRunner: { run }, + }); + testScheduler.addToSchedule({ + id: '2', + task: jest.fn(), + scheduledRunner: { run }, + }); + + // Starts scheduling process + testScheduler.start(); + + const signals = run.mock.calls.map(([options]) => options.signal); + + expect(signals).toHaveLength(2); + + for (const signal of signals) { + expect(signal.aborted).toBeFalsy(); + } + + // Stops scheduling process + testScheduler.stop(); + + for (const signal of signals) { + expect(signal.aborted).toBeTruthy(); + } + }); + }); }); diff --git a/plugins/search-backend-node/src/Scheduler.ts b/plugins/search-backend-node/src/Scheduler.ts index 872a410808..82d45df090 100644 --- a/plugins/search-backend-node/src/Scheduler.ts +++ b/plugins/search-backend-node/src/Scheduler.ts @@ -40,13 +40,13 @@ export type ScheduleTaskParameters = { export class Scheduler { private logger: Logger; private schedule: { [id: string]: TaskEnvelope }; - private abortController: AbortController; + private abortControllers: AbortController[]; private isRunning: boolean; constructor(options: { logger: Logger }) { this.logger = options.logger; this.schedule = {}; - this.abortController = new AbortController(); + this.abortControllers = []; this.isRunning = false; } @@ -78,11 +78,13 @@ export class Scheduler { this.logger.info('Starting all scheduled search tasks.'); this.isRunning = true; Object.keys(this.schedule).forEach(id => { + const abortController = new AbortController(); + this.abortControllers.push(abortController); const { task, scheduledRunner } = this.schedule[id]; scheduledRunner.run({ id, fn: task, - signal: this.abortController.signal, + signal: abortController.signal, }); }); } @@ -92,7 +94,10 @@ export class Scheduler { */ stop() { this.logger.info('Stopping all scheduled search tasks.'); - this.abortController.abort(); + for (const abortController of this.abortControllers) { + abortController.abort(); + } + this.abortControllers = []; this.isRunning = false; } }