@open-mercato/queue 0.4.6-main-be1825c150 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/worker/runner.js +43 -13
- package/dist/worker/runner.js.map +2 -2
- package/package.json +2 -3
- package/src/worker/__tests__/runner.test.ts +83 -0
- package/src/worker/runner.ts +56 -15
package/dist/worker/runner.js
CHANGED
|
@@ -1,4 +1,45 @@
|
|
|
1
1
|
import { createQueue } from "../factory.js";
|
|
2
|
+
const managedQueues = /* @__PURE__ */ new Set();
|
|
3
|
+
let shutdownHandlersRegistered = false;
|
|
4
|
+
let shutdownInProgress = false;
|
|
5
|
+
function unregisterShutdownHandlers(sigtermHandler, sigintHandler) {
|
|
6
|
+
process.off("SIGTERM", sigtermHandler);
|
|
7
|
+
process.off("SIGINT", sigintHandler);
|
|
8
|
+
shutdownHandlersRegistered = false;
|
|
9
|
+
}
|
|
10
|
+
function registerShutdownHandlers() {
|
|
11
|
+
if (shutdownHandlersRegistered) return;
|
|
12
|
+
const shutdown = async (signal) => {
|
|
13
|
+
if (shutdownInProgress) return;
|
|
14
|
+
shutdownInProgress = true;
|
|
15
|
+
console.log(`[worker] Received ${signal}, shutting down gracefully...`);
|
|
16
|
+
let hasError = false;
|
|
17
|
+
for (const queue of managedQueues) {
|
|
18
|
+
try {
|
|
19
|
+
await queue.close();
|
|
20
|
+
} catch (error) {
|
|
21
|
+
hasError = true;
|
|
22
|
+
console.error("[worker] Error during shutdown:", error);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
managedQueues.clear();
|
|
26
|
+
unregisterShutdownHandlers(sigtermHandler, sigintHandler);
|
|
27
|
+
shutdownInProgress = false;
|
|
28
|
+
if (!hasError) {
|
|
29
|
+
console.log("[worker] Worker closed successfully");
|
|
30
|
+
}
|
|
31
|
+
process.exit(hasError ? 1 : 0);
|
|
32
|
+
};
|
|
33
|
+
const sigtermHandler = () => {
|
|
34
|
+
void shutdown("SIGTERM");
|
|
35
|
+
};
|
|
36
|
+
const sigintHandler = () => {
|
|
37
|
+
void shutdown("SIGINT");
|
|
38
|
+
};
|
|
39
|
+
process.on("SIGTERM", sigtermHandler);
|
|
40
|
+
process.on("SIGINT", sigintHandler);
|
|
41
|
+
shutdownHandlersRegistered = true;
|
|
42
|
+
}
|
|
2
43
|
async function runWorker(options) {
|
|
3
44
|
const {
|
|
4
45
|
queueName,
|
|
@@ -16,19 +57,8 @@ async function runWorker(options) {
|
|
|
16
57
|
concurrency
|
|
17
58
|
});
|
|
18
59
|
if (gracefulShutdown) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
await queue.close();
|
|
23
|
-
console.log("[worker] Worker closed successfully");
|
|
24
|
-
process.exit(0);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.error("[worker] Error during shutdown:", error);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
31
|
-
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
60
|
+
managedQueues.add(queue);
|
|
61
|
+
registerShutdownHandlers();
|
|
32
62
|
}
|
|
33
63
|
await queue.process(handler);
|
|
34
64
|
console.log(`[worker] Worker running with concurrency ${concurrency}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/worker/runner.ts"],
|
|
4
|
-
"sourcesContent": ["import { createQueue } from '../factory'\nimport type { JobHandler, AsyncQueueOptions, QueueStrategyType } from '../types'\n\n/**\n * Options for running a queue worker.\n */\nexport type WorkerRunnerOptions<T = unknown> = {\n /** Name of the queue to process */\n queueName: string\n /** Handler function to process each job */\n handler: JobHandler<T>\n /** Redis connection options (only used for async strategy) */\n connection?: AsyncQueueOptions['connection']\n /** Number of concurrent jobs to process */\n concurrency?: number\n /** Whether to set up graceful shutdown handlers */\n gracefulShutdown?: boolean\n /** If true, don't block - return immediately after starting processing (for multi-queue mode) */\n background?: boolean\n /** Queue strategy to use. Defaults to QUEUE_STRATEGY env var or 'local' */\n strategy?: QueueStrategyType\n}\n\n/**\n * Runs a queue worker that processes jobs continuously.\n *\n * This function:\n * 1. Creates an async queue instance\n * 2. Starts a BullMQ worker\n * 3. Sets up graceful shutdown on SIGTERM/SIGINT\n * 4. Keeps the process running until shutdown\n *\n * @template T - The job payload type\n * @param options - Worker configuration\n *\n * @example\n * ```typescript\n * import { runWorker } from '@open-mercato/queue/worker'\n *\n * await runWorker({\n * queueName: 'events',\n * handler: async (job, ctx) => {\n * console.log(`Processing ${ctx.jobId}:`, job.payload)\n * },\n * connection: { url: process.env.REDIS_URL },\n * concurrency: 5,\n * })\n * ```\n */\nexport async function runWorker<T = unknown>(\n options: WorkerRunnerOptions<T>\n): Promise<void> {\n const {\n queueName,\n handler,\n connection,\n concurrency = 1,\n gracefulShutdown = true,\n background = false,\n strategy: strategyOption,\n } = options\n\n // Determine queue strategy from option, env var, or default to 'local'\n const strategy: QueueStrategyType = strategyOption\n ?? (process.env.QUEUE_STRATEGY === 'async' ? 'async' : 'local')\n\n console.log(`[worker] Starting worker for queue \"${queueName}\" (strategy: ${strategy})...`)\n\n const queue = createQueue<T>(queueName, strategy, {\n connection,\n concurrency,\n })\n\n // Set up graceful shutdown\n if (gracefulShutdown) {\n
|
|
5
|
-
"mappings": "AAAA,SAAS,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import { createQueue } from '../factory'\nimport type { Queue, JobHandler, AsyncQueueOptions, QueueStrategyType } from '../types'\n\n/**\n * Options for running a queue worker.\n */\nexport type WorkerRunnerOptions<T = unknown> = {\n /** Name of the queue to process */\n queueName: string\n /** Handler function to process each job */\n handler: JobHandler<T>\n /** Redis connection options (only used for async strategy) */\n connection?: AsyncQueueOptions['connection']\n /** Number of concurrent jobs to process */\n concurrency?: number\n /** Whether to set up graceful shutdown handlers */\n gracefulShutdown?: boolean\n /** If true, don't block - return immediately after starting processing (for multi-queue mode) */\n background?: boolean\n /** Queue strategy to use. Defaults to QUEUE_STRATEGY env var or 'local' */\n strategy?: QueueStrategyType\n}\n\nconst managedQueues = new Set<Queue<unknown>>()\nlet shutdownHandlersRegistered = false\nlet shutdownInProgress = false\n\nfunction unregisterShutdownHandlers(sigtermHandler: () => void, sigintHandler: () => void): void {\n process.off('SIGTERM', sigtermHandler)\n process.off('SIGINT', sigintHandler)\n shutdownHandlersRegistered = false\n}\n\nfunction registerShutdownHandlers(): void {\n if (shutdownHandlersRegistered) return\n\n const shutdown = async (signal: string) => {\n if (shutdownInProgress) return\n shutdownInProgress = true\n\n console.log(`[worker] Received ${signal}, shutting down gracefully...`)\n\n let hasError = false\n for (const queue of managedQueues) {\n try {\n await queue.close()\n } catch (error) {\n hasError = true\n console.error('[worker] Error during shutdown:', error)\n }\n }\n\n managedQueues.clear()\n unregisterShutdownHandlers(sigtermHandler, sigintHandler)\n shutdownInProgress = false\n\n if (!hasError) {\n console.log('[worker] Worker closed successfully')\n }\n\n process.exit(hasError ? 1 : 0)\n }\n\n const sigtermHandler = () => {\n void shutdown('SIGTERM')\n }\n\n const sigintHandler = () => {\n void shutdown('SIGINT')\n }\n\n process.on('SIGTERM', sigtermHandler)\n process.on('SIGINT', sigintHandler)\n shutdownHandlersRegistered = true\n}\n\n/**\n * Runs a queue worker that processes jobs continuously.\n *\n * This function:\n * 1. Creates an async queue instance\n * 2. Starts a BullMQ worker\n * 3. Sets up graceful shutdown on SIGTERM/SIGINT\n * 4. Keeps the process running until shutdown\n *\n * @template T - The job payload type\n * @param options - Worker configuration\n *\n * @example\n * ```typescript\n * import { runWorker } from '@open-mercato/queue/worker'\n *\n * await runWorker({\n * queueName: 'events',\n * handler: async (job, ctx) => {\n * console.log(`Processing ${ctx.jobId}:`, job.payload)\n * },\n * connection: { url: process.env.REDIS_URL },\n * concurrency: 5,\n * })\n * ```\n */\nexport async function runWorker<T = unknown>(\n options: WorkerRunnerOptions<T>\n): Promise<void> {\n const {\n queueName,\n handler,\n connection,\n concurrency = 1,\n gracefulShutdown = true,\n background = false,\n strategy: strategyOption,\n } = options\n\n // Determine queue strategy from option, env var, or default to 'local'\n const strategy: QueueStrategyType = strategyOption\n ?? (process.env.QUEUE_STRATEGY === 'async' ? 'async' : 'local')\n\n console.log(`[worker] Starting worker for queue \"${queueName}\" (strategy: ${strategy})...`)\n\n const queue = createQueue<T>(queueName, strategy, {\n connection,\n concurrency,\n })\n\n // Set up graceful shutdown\n if (gracefulShutdown) {\n managedQueues.add(queue as Queue<unknown>)\n registerShutdownHandlers()\n }\n\n // Start processing\n await queue.process(handler)\n\n console.log(`[worker] Worker running with concurrency ${concurrency}`)\n\n if (background) {\n // Return immediately for multi-queue mode\n return\n }\n\n console.log('[worker] Press Ctrl+C to stop')\n\n // Keep the process alive (single-queue mode)\n await new Promise(() => {\n // This promise never resolves, keeping the worker running\n })\n}\n\n/**\n * Creates a worker handler that routes jobs to specific handlers based on job type.\n *\n * @template T - Base job payload type (must include a 'type' field)\n * @param handlers - Map of job types to their handlers\n *\n * @example\n * ```typescript\n * const handler = createRoutedHandler({\n * 'user.created': async (job) => { ... },\n * 'order.placed': async (job) => { ... },\n * })\n *\n * await runWorker({ queueName: 'events', handler })\n * ```\n */\nexport function createRoutedHandler<T extends { type: string }>(\n handlers: Record<string, JobHandler<T>>\n): JobHandler<T> {\n return async (job, ctx) => {\n const type = job.payload.type\n const handler = handlers[type]\n\n if (!handler) {\n console.warn(`[worker] No handler registered for job type \"${type}\"`)\n return\n }\n\n await handler(job, ctx)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,mBAAmB;AAuB5B,MAAM,gBAAgB,oBAAI,IAAoB;AAC9C,IAAI,6BAA6B;AACjC,IAAI,qBAAqB;AAEzB,SAAS,2BAA2B,gBAA4B,eAAiC;AAC/F,UAAQ,IAAI,WAAW,cAAc;AACrC,UAAQ,IAAI,UAAU,aAAa;AACnC,+BAA6B;AAC/B;AAEA,SAAS,2BAAiC;AACxC,MAAI,2BAA4B;AAEhC,QAAM,WAAW,OAAO,WAAmB;AACzC,QAAI,mBAAoB;AACxB,yBAAqB;AAErB,YAAQ,IAAI,qBAAqB,MAAM,+BAA+B;AAEtE,QAAI,WAAW;AACf,eAAW,SAAS,eAAe;AACjC,UAAI;AACF,cAAM,MAAM,MAAM;AAAA,MACpB,SAAS,OAAO;AACd,mBAAW;AACX,gBAAQ,MAAM,mCAAmC,KAAK;AAAA,MACxD;AAAA,IACF;AAEA,kBAAc,MAAM;AACpB,+BAA2B,gBAAgB,aAAa;AACxD,yBAAqB;AAErB,QAAI,CAAC,UAAU;AACb,cAAQ,IAAI,qCAAqC;AAAA,IACnD;AAEA,YAAQ,KAAK,WAAW,IAAI,CAAC;AAAA,EAC/B;AAEA,QAAM,iBAAiB,MAAM;AAC3B,SAAK,SAAS,SAAS;AAAA,EACzB;AAEA,QAAM,gBAAgB,MAAM;AAC1B,SAAK,SAAS,QAAQ;AAAA,EACxB;AAEA,UAAQ,GAAG,WAAW,cAAc;AACpC,UAAQ,GAAG,UAAU,aAAa;AAClC,+BAA6B;AAC/B;AA4BA,eAAsB,UACpB,SACe;AACf,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,aAAa;AAAA,IACb,UAAU;AAAA,EACZ,IAAI;AAGJ,QAAM,WAA8B,mBAC9B,QAAQ,IAAI,mBAAmB,UAAU,UAAU;AAEzD,UAAQ,IAAI,uCAAuC,SAAS,gBAAgB,QAAQ,MAAM;AAE1F,QAAM,QAAQ,YAAe,WAAW,UAAU;AAAA,IAChD;AAAA,IACA;AAAA,EACF,CAAC;AAGD,MAAI,kBAAkB;AACpB,kBAAc,IAAI,KAAuB;AACzC,6BAAyB;AAAA,EAC3B;AAGA,QAAM,MAAM,QAAQ,OAAO;AAE3B,UAAQ,IAAI,4CAA4C,WAAW,EAAE;AAErE,MAAI,YAAY;AAEd;AAAA,EACF;AAEA,UAAQ,IAAI,+BAA+B;AAG3C,QAAM,IAAI,QAAQ,MAAM;AAAA,EAExB,CAAC;AACH;AAkBO,SAAS,oBACd,UACe;AACf,SAAO,OAAO,KAAK,QAAQ;AACzB,UAAM,OAAO,IAAI,QAAQ;AACzB,UAAM,UAAU,SAAS,IAAI;AAE7B,QAAI,CAAC,SAAS;AACZ,cAAQ,KAAK,gDAAgD,IAAI,GAAG;AACpE;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,GAAG;AAAA,EACxB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/queue",
|
|
3
|
-
"version": "0.4.6
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Multi-strategy job queue with local and BullMQ support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -48,6 +48,5 @@
|
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
|
51
|
-
}
|
|
52
|
-
"stableVersion": "0.4.5"
|
|
51
|
+
}
|
|
53
52
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createQueue } from '../../factory'
|
|
2
|
+
import type { Queue } from '../../types'
|
|
3
|
+
import { runWorker } from '../runner'
|
|
4
|
+
|
|
5
|
+
jest.mock('../../factory', () => ({
|
|
6
|
+
createQueue: jest.fn(),
|
|
7
|
+
}))
|
|
8
|
+
|
|
9
|
+
function buildFakeQueue(name: string): Queue<unknown> {
|
|
10
|
+
return {
|
|
11
|
+
name,
|
|
12
|
+
strategy: 'local',
|
|
13
|
+
enqueue: jest.fn(async () => 'job-id'),
|
|
14
|
+
process: jest.fn(async () => ({ processed: -1, failed: -1, lastJobId: undefined })),
|
|
15
|
+
clear: jest.fn(async () => ({ removed: 0 })),
|
|
16
|
+
close: jest.fn(async () => {}),
|
|
17
|
+
getJobCounts: jest.fn(async () => ({ waiting: 0, active: 0, completed: 0, failed: 0 })),
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('runWorker', () => {
|
|
22
|
+
const createQueueMock = createQueue as jest.MockedFunction<typeof createQueue>
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
jest.clearAllMocks()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('registers SIGINT/SIGTERM shutdown handlers only once and closes all queues', async () => {
|
|
29
|
+
const queueA = buildFakeQueue('queue-a')
|
|
30
|
+
const queueB = buildFakeQueue('queue-b')
|
|
31
|
+
createQueueMock.mockReturnValueOnce(queueA).mockReturnValueOnce(queueB)
|
|
32
|
+
|
|
33
|
+
const exitSpy = jest.spyOn(process, 'exit').mockImplementation((() => undefined) as never)
|
|
34
|
+
const initialSigtermListeners = process.listenerCount('SIGTERM')
|
|
35
|
+
const initialSigintListeners = process.listenerCount('SIGINT')
|
|
36
|
+
|
|
37
|
+
await runWorker({
|
|
38
|
+
queueName: 'queue-a',
|
|
39
|
+
handler: async () => {},
|
|
40
|
+
background: true,
|
|
41
|
+
gracefulShutdown: true,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
await runWorker({
|
|
45
|
+
queueName: 'queue-b',
|
|
46
|
+
handler: async () => {},
|
|
47
|
+
background: true,
|
|
48
|
+
gracefulShutdown: true,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
expect(process.listenerCount('SIGTERM')).toBe(initialSigtermListeners + 1)
|
|
52
|
+
expect(process.listenerCount('SIGINT')).toBe(initialSigintListeners + 1)
|
|
53
|
+
|
|
54
|
+
const sigtermHandler = process.listeners('SIGTERM')[initialSigtermListeners] as (() => void) | undefined
|
|
55
|
+
sigtermHandler?.()
|
|
56
|
+
await new Promise<void>((resolve) => setImmediate(resolve))
|
|
57
|
+
|
|
58
|
+
expect(queueA.close).toHaveBeenCalledTimes(1)
|
|
59
|
+
expect(queueB.close).toHaveBeenCalledTimes(1)
|
|
60
|
+
expect(exitSpy).toHaveBeenCalledWith(0)
|
|
61
|
+
expect(process.listenerCount('SIGTERM')).toBe(initialSigtermListeners)
|
|
62
|
+
expect(process.listenerCount('SIGINT')).toBe(initialSigintListeners)
|
|
63
|
+
|
|
64
|
+
exitSpy.mockRestore()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('does not register shutdown handlers when gracefulShutdown is disabled', async () => {
|
|
68
|
+
createQueueMock.mockReturnValueOnce(buildFakeQueue('queue-c'))
|
|
69
|
+
|
|
70
|
+
const initialSigtermListeners = process.listenerCount('SIGTERM')
|
|
71
|
+
const initialSigintListeners = process.listenerCount('SIGINT')
|
|
72
|
+
|
|
73
|
+
await runWorker({
|
|
74
|
+
queueName: 'queue-c',
|
|
75
|
+
handler: async () => {},
|
|
76
|
+
background: true,
|
|
77
|
+
gracefulShutdown: false,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
expect(process.listenerCount('SIGTERM')).toBe(initialSigtermListeners)
|
|
81
|
+
expect(process.listenerCount('SIGINT')).toBe(initialSigintListeners)
|
|
82
|
+
})
|
|
83
|
+
})
|
package/src/worker/runner.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createQueue } from '../factory'
|
|
2
|
-
import type { JobHandler, AsyncQueueOptions, QueueStrategyType } from '../types'
|
|
2
|
+
import type { Queue, JobHandler, AsyncQueueOptions, QueueStrategyType } from '../types'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Options for running a queue worker.
|
|
@@ -21,6 +21,59 @@ export type WorkerRunnerOptions<T = unknown> = {
|
|
|
21
21
|
strategy?: QueueStrategyType
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
const managedQueues = new Set<Queue<unknown>>()
|
|
25
|
+
let shutdownHandlersRegistered = false
|
|
26
|
+
let shutdownInProgress = false
|
|
27
|
+
|
|
28
|
+
function unregisterShutdownHandlers(sigtermHandler: () => void, sigintHandler: () => void): void {
|
|
29
|
+
process.off('SIGTERM', sigtermHandler)
|
|
30
|
+
process.off('SIGINT', sigintHandler)
|
|
31
|
+
shutdownHandlersRegistered = false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function registerShutdownHandlers(): void {
|
|
35
|
+
if (shutdownHandlersRegistered) return
|
|
36
|
+
|
|
37
|
+
const shutdown = async (signal: string) => {
|
|
38
|
+
if (shutdownInProgress) return
|
|
39
|
+
shutdownInProgress = true
|
|
40
|
+
|
|
41
|
+
console.log(`[worker] Received ${signal}, shutting down gracefully...`)
|
|
42
|
+
|
|
43
|
+
let hasError = false
|
|
44
|
+
for (const queue of managedQueues) {
|
|
45
|
+
try {
|
|
46
|
+
await queue.close()
|
|
47
|
+
} catch (error) {
|
|
48
|
+
hasError = true
|
|
49
|
+
console.error('[worker] Error during shutdown:', error)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
managedQueues.clear()
|
|
54
|
+
unregisterShutdownHandlers(sigtermHandler, sigintHandler)
|
|
55
|
+
shutdownInProgress = false
|
|
56
|
+
|
|
57
|
+
if (!hasError) {
|
|
58
|
+
console.log('[worker] Worker closed successfully')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
process.exit(hasError ? 1 : 0)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const sigtermHandler = () => {
|
|
65
|
+
void shutdown('SIGTERM')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const sigintHandler = () => {
|
|
69
|
+
void shutdown('SIGINT')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
process.on('SIGTERM', sigtermHandler)
|
|
73
|
+
process.on('SIGINT', sigintHandler)
|
|
74
|
+
shutdownHandlersRegistered = true
|
|
75
|
+
}
|
|
76
|
+
|
|
24
77
|
/**
|
|
25
78
|
* Runs a queue worker that processes jobs continuously.
|
|
26
79
|
*
|
|
@@ -73,20 +126,8 @@ export async function runWorker<T = unknown>(
|
|
|
73
126
|
|
|
74
127
|
// Set up graceful shutdown
|
|
75
128
|
if (gracefulShutdown) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
await queue.close()
|
|
80
|
-
console.log('[worker] Worker closed successfully')
|
|
81
|
-
process.exit(0)
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error('[worker] Error during shutdown:', error)
|
|
84
|
-
process.exit(1)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
process.on('SIGTERM', () => shutdown('SIGTERM'))
|
|
89
|
-
process.on('SIGINT', () => shutdown('SIGINT'))
|
|
129
|
+
managedQueues.add(queue as Queue<unknown>)
|
|
130
|
+
registerShutdownHandlers()
|
|
90
131
|
}
|
|
91
132
|
|
|
92
133
|
// Start processing
|