fix(worker): close all Queue singletons + promotion intervals on SIGTERM (issue #94 bugs 4, 7, 10)

This commit is contained in:
Zac Gaetano 2026-05-26 07:38:08 -04:00
parent 6eb98d866b
commit bacdb9f49c

View file

@ -1,9 +1,9 @@
import 'dotenv/config';
import { Worker } from 'bullmq';
import { proxyWorker } from './workers/proxy.js';
import { proxyWorker, thumbnailQueue as proxyThumbnailQueue } from './workers/proxy.js';
import { thumbnailWorker } from './workers/thumbnail.js';
import { conformWorker } from './workers/conform.js';
import { youtubeImportWorker } from './workers/youtube-import.js';
import { youtubeImportWorker, proxyQueue as youtubeProxyQueue } from './workers/youtube-import.js';
import { trimWorker } from './workers/trimWorker.js';
import { startPromotionWorker } from './workers/promotion.js';
@ -77,7 +77,9 @@ const workers = [
console.log(`Concurrency: proxy=${PROXY_CONCURRENCY} thumbnail=${THUMBNAIL_CONCURRENCY} conform=${CONFORM_CONCURRENCY} trim=${TRIM_CONCURRENCY} import=1`);
startPromotionWorker();
// BUG FIX #4: startPromotionWorker() now returns a shutdown function that
// clears the poll intervals and closes the promotion proxyQueue singleton.
const stopPromotionWorker = startPromotionWorker();
console.log('Wild Dragon Worker Service started');
console.log(`Redis: ${redisOptions.host}:${redisOptions.port}`);
@ -86,6 +88,24 @@ console.log('Background scans: promotion (growing-files → S3)');
process.on('SIGTERM', async () => {
console.log('SIGTERM received, shutting down...');
await Promise.all(workers.map(w => w.close()));
// BUG FIX #4 + #10: Close all BullMQ Workers AND all Queue client instances
// on SIGTERM. Workers process jobs; Queues dispatch them. Both hold open
// Redis connections that keep the event loop alive after workers are closed,
// causing the process to hang indefinitely unless process.exit() is called.
// Explicitly closing every Queue allows the event loop to drain naturally.
await Promise.all([
// Close all Worker instances (stops accepting new jobs, waits for active)
...workers.map(w => w.close()),
// BUG FIX #7: Close the Queue singletons from worker modules.
// proxyThumbnailQueue: thumbnailQueue in proxy.js (dispatches thumbnail jobs)
// youtubeProxyQueue: proxyQueue in youtube-import.js (dispatches proxy jobs)
proxyThumbnailQueue.close().catch(() => {}),
youtubeProxyQueue.close().catch(() => {}),
// BUG FIX #4: Stop the promotion worker intervals and close its proxyQueue
stopPromotionWorker(),
]);
console.log('All workers and queues closed');
process.exit(0);
});