@@ -12,27 +12,38 @@ async function generateUniqueSlug(baseSlug: string): Promise<string> {
1212 return `${ baseSlug } -${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . substr ( 2 , 5 ) } `
1313}
1414
15- // BullMQ requires ioredis - create dedicated connection for queues
16- // Use centralized Redis config with BullMQ-specific settings
17- const connection = createRedisConnection ( {
15+ // BullMQ connection options (shared base config)
16+ const bullmqConnectionOptions = {
1817 maxRetriesPerRequest : null , // BullMQ requirement - allows unlimited retries
19- enableReadyCheck : true , // Enable to ensure connection is ready before sending commands
20- enableOfflineQueue : true , // Enable to prevent command timeouts during reconnection
21- // Explicit timeout for BullMQ operations (longer than default for heavy operations)
18+ enableReadyCheck : true ,
19+ enableOfflineQueue : true ,
2220 commandTimeout : parseInt ( process . env . REDIS_COMMAND_TIMEOUT || '60000' , 10 ) ,
23- // Connection pooling settings
2421 connectTimeout : parseInt ( process . env . REDIS_CONNECT_TIMEOUT || '30000' , 10 ) ,
2522 keepAlive : parseInt ( process . env . REDIS_KEEPALIVE || '30000' , 10 )
26- } )
23+ }
24+
25+ // Queue connection (non-blocking operations)
26+ const queueConnection = createRedisConnection ( bullmqConnectionOptions )
2727
28- if ( ! connection ) {
29- queueLogger . error ( 'Failed to create Redis connection for BullMQ' )
28+ if ( ! queueConnection ) {
29+ queueLogger . error ( 'Failed to create Redis connection for BullMQ Queue ' )
3030 throw new Error ( 'Redis configuration required for queue operations' )
3131}
3232
33+ // Worker connection (blocking operations - MUST be separate)
34+ // BullMQ uses BRPOPLPUSH which blocks the connection
35+ const createWorkerConnection = ( ) => {
36+ const conn = createRedisConnection ( bullmqConnectionOptions )
37+ if ( ! conn ) {
38+ queueLogger . error ( 'Failed to create Redis connection for BullMQ Worker' )
39+ throw new Error ( 'Redis configuration required for worker operations' )
40+ }
41+ return conn
42+ }
43+
3344// Main queue for storing documents
3445export const StoreDocumentQueue = new Queue < StoreDocumentData > ( 'store-documents' , {
35- connection,
46+ connection : queueConnection ,
3647 defaultJobOptions : {
3748 attempts : 5 , // Increased retry attempts for better reliability
3849 backoff : {
@@ -49,7 +60,7 @@ export const StoreDocumentQueue = new Queue<StoreDocumentData>('store-documents'
4960
5061// Dead Letter Queue for permanently failed jobs
5162export const DeadLetterQueue = new Queue < StoreDocumentData > ( 'store-documents-dlq' , {
52- connection,
63+ connection : queueConnection ,
5364 defaultJobOptions : {
5465 removeOnComplete : {
5566 count : 500 ,
@@ -67,6 +78,8 @@ StoreDocumentQueue.on('error', (err: Error) => {
6778// Worker to process document storage jobs
6879export const createDocumentWorker = ( ) => {
6980 const redisPublisher = getRedisPublisher ( )
81+ // Worker MUST have dedicated connection (uses blocking commands)
82+ const workerConnection = createWorkerConnection ( )
7083
7184 const worker = new Worker < StoreDocumentData > (
7285 'store-documents' ,
@@ -119,7 +132,10 @@ export const createDocumentWorker = () => {
119132 } )
120133
121134 const duration = Date . now ( ) - startTime
122- queueLogger . info ( { jobId : job . id , duration : `${ duration } ms` } , 'Document stored successfully' )
135+ queueLogger . info (
136+ { jobId : job . id , duration : `${ duration } ms` } ,
137+ 'Document stored successfully'
138+ )
123139
124140 // Publish save confirmation to document-specific Redis channel
125141 if ( redisPublisher ) {
@@ -152,7 +168,7 @@ export const createDocumentWorker = () => {
152168 }
153169 } ,
154170 {
155- connection,
171+ connection : workerConnection ,
156172 concurrency : parseInt ( process . env . BULLMQ_CONCURRENCY || '5' , 10 ) ,
157173 limiter : {
158174 max : parseInt ( process . env . BULLMQ_RATE_LIMIT_MAX || '300' , 10 ) ,
0 commit comments