diff --git a/packages/cli/src/Queue.ts b/packages/cli/src/Queue.ts index c7aacaab6d..8fd435100b 100644 --- a/packages/cli/src/Queue.ts +++ b/packages/cli/src/Queue.ts @@ -9,7 +9,8 @@ import { } from 'n8n-workflow'; import { ActiveExecutions } from '@/ActiveExecutions'; import config from '@/config'; -import { HIGHEST_PRIORITY, OnShutdown } from './decorators/OnShutdown'; +import { OnShutdown } from './decorators/OnShutdown'; +import { HIGHEST_SHUTDOWN_PRIORITY } from './constants'; export type JobId = Bull.JobId; export type Job = Bull.Job; @@ -109,7 +110,7 @@ export class Queue { return await this.jobQueue.client.ping(); } - @OnShutdown(HIGHEST_PRIORITY) + @OnShutdown(HIGHEST_SHUTDOWN_PRIORITY) // Stop accepting new jobs, `doNotWaitActive` allows reporting progress async pause(): Promise { return await this.jobQueue?.pause(true, true); diff --git a/packages/cli/src/constants.ts b/packages/cli/src/constants.ts index 28b27d1b38..41c95cb01a 100644 --- a/packages/cli/src/constants.ts +++ b/packages/cli/src/constants.ts @@ -165,3 +165,7 @@ export const ARTIFICIAL_TASK_DATA = { ], ], }; + +export const LOWEST_SHUTDOWN_PRIORITY = 0; +export const DEFAULT_SHUTDOWN_PRIORITY = 100; +export const HIGHEST_SHUTDOWN_PRIORITY = 200; diff --git a/packages/cli/src/decorators/OnShutdown.ts b/packages/cli/src/decorators/OnShutdown.ts index c5177a295b..68b3cbb4ca 100644 --- a/packages/cli/src/decorators/OnShutdown.ts +++ b/packages/cli/src/decorators/OnShutdown.ts @@ -1,10 +1,7 @@ import { Container } from 'typedi'; import { ApplicationError } from 'n8n-workflow'; import { type ServiceClass, ShutdownService } from '@/shutdown/Shutdown.service'; - -export const LOWEST_PRIORITY = 0; -export const DEFAULT_PRIORITY = 100; -export const HIGHEST_PRIORITY = 200; +import { DEFAULT_SHUTDOWN_PRIORITY } from '@/constants'; /** * Decorator that registers a method as a shutdown hook. The method will @@ -26,7 +23,7 @@ export const HIGHEST_PRIORITY = 200; * ``` */ export const OnShutdown = - (priority = DEFAULT_PRIORITY): MethodDecorator => + (priority = DEFAULT_SHUTDOWN_PRIORITY): MethodDecorator => (prototype, propertyKey, descriptor) => { const serviceClass = prototype.constructor as ServiceClass; const methodName = String(propertyKey); diff --git a/packages/cli/src/services/redis/redis-client.service.ts b/packages/cli/src/services/redis/redis-client.service.ts index 3a282c02bc..7363a9c9b7 100644 --- a/packages/cli/src/services/redis/redis-client.service.ts +++ b/packages/cli/src/services/redis/redis-client.service.ts @@ -4,7 +4,8 @@ import { Logger } from '@/Logger'; import ioRedis from 'ioredis'; import type { Cluster, RedisOptions } from 'ioredis'; import type { RedisClientType } from './RedisServiceBaseClasses'; -import { LOWEST_PRIORITY, OnShutdown } from '@/decorators/OnShutdown'; +import { OnShutdown } from '@/decorators/OnShutdown'; +import { LOWEST_SHUTDOWN_PRIORITY } from '@/constants'; @Service() export class RedisClientService { @@ -23,7 +24,7 @@ export class RedisClientService { return client; } - @OnShutdown(LOWEST_PRIORITY) + @OnShutdown(LOWEST_SHUTDOWN_PRIORITY) disconnectClients() { for (const client of this.clients) { client.disconnect(); diff --git a/packages/cli/src/shutdown/Shutdown.service.ts b/packages/cli/src/shutdown/Shutdown.service.ts index 1dd858f271..772463f23a 100644 --- a/packages/cli/src/shutdown/Shutdown.service.ts +++ b/packages/cli/src/shutdown/Shutdown.service.ts @@ -2,6 +2,7 @@ import { Container, Service } from 'typedi'; import { ApplicationError, ErrorReporterProxy, assert } from 'n8n-workflow'; import type { Class } from 'n8n-core'; import { Logger } from '@/Logger'; +import { LOWEST_SHUTDOWN_PRIORITY, HIGHEST_SHUTDOWN_PRIORITY } from '@/constants'; type HandlerFn = () => Promise | void; export type ServiceClass = Class>; @@ -33,6 +34,13 @@ export class ShutdownService { /** Registers given listener to be notified when the application is shutting down */ register(priority: number, handler: ShutdownHandler) { + if (priority < LOWEST_SHUTDOWN_PRIORITY || priority > HIGHEST_SHUTDOWN_PRIORITY) { + throw new ApplicationError( + `Invalid shutdown priority. Please set it between ${LOWEST_SHUTDOWN_PRIORITY} and ${HIGHEST_SHUTDOWN_PRIORITY}.`, + { extra: { priority } }, + ); + } + if (!this.handlersByPriority[priority]) { this.handlersByPriority[priority] = []; } diff --git a/packages/cli/test/unit/decorators/OnShutdown.test.ts b/packages/cli/test/unit/decorators/OnShutdown.test.ts index efef660a21..833915f4cb 100644 --- a/packages/cli/test/unit/decorators/OnShutdown.test.ts +++ b/packages/cli/test/unit/decorators/OnShutdown.test.ts @@ -73,4 +73,26 @@ describe('OnShutdown', () => { new TestClass(); }).toThrow('TestClass.onShutdown() must be a method on TestClass to use "OnShutdown"'); }); + + it('should throw if the priority is invalid', () => { + expect(() => { + @Service() + class TestClass { + @OnShutdown(201) + async onShutdown() {} + } + + new TestClass(); + }).toThrow('Invalid shutdown priority. Please set it between 0 and 200.'); + + expect(() => { + @Service() + class TestClass { + @OnShutdown(-1) + async onShutdown() {} + } + + new TestClass(); + }).toThrow('Invalid shutdown priority. Please set it between 0 and 200.'); + }); });