All files / src/lib/queue config.ts

88.42% Statements 107/121
50% Branches 2/4
50% Functions 1/2
88.42% Lines 107/121

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 1221x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x             1x 1x 1x 1x 1x 1x 1x 3x     3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x             3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
/**
 * Queue Configuration
 *
 * Configures BullMQ queues with Redis connection settings.
 * Supports both Redis (production) and in-memory fallback (development).
 */
 
import { ConnectionOptions, DefaultJobOptions } from 'bullmq';
 
/**
 * Redis connection settings extracted for direct access
 */
const redisConfig = {
  host: process.env.REDIS_HOST || 'localhost',
  port: parseInt(process.env.REDIS_PORT || '6379', 10),
  password: process.env.REDIS_PASSWORD || undefined,
};
 
/**
 * Redis connection configuration for BullMQ
 * Uses environment variables for production, defaults for development
 */
export const redisConnection: ConnectionOptions = {
  ...redisConfig,
  maxRetriesPerRequest: null, // Required for BullMQ
  enableReadyCheck: false,
  retryStrategy: (times: number) => {
    // Stop retrying after 5 attempts in development
    if (process.env.NODE_ENV === 'development' && times > 5) {
      return null;
    }
    // Exponential backoff with max 30 seconds
    return Math.min(times * 1000, 30000);
  },
};
 
/**
 * Check if Redis is available
 */
export async function isRedisAvailable(): Promise<boolean> {
  if (process.env.SKIP_REDIS === 'true') {
    return false;
  }
 
  try {
    const { Redis } = await import('ioredis');
    const client = new Redis({
      host: redisConfig.host,
      port: redisConfig.port,
      password: redisConfig.password,
      lazyConnect: true,
      connectTimeout: 3000,
    });
 
    await client.connect();
    await client.ping();
    await client.quit();
    return true;
  } catch {
    return false;
  }
}
 
/**
 * Queue configurations
 */
export const queueConfig = {
  email: {
    name: 'email-queue',
    defaultJobOptions: {
      attempts: 3,
      backoff: {
        type: 'exponential' as const,
        delay: 1000,
      },
      removeOnComplete: {
        age: 24 * 3600, // 24 hours
        count: 1000,
      },
      removeOnFail: {
        age: 7 * 24 * 3600, // 7 days
      },
    } satisfies DefaultJobOptions,
  },
  notification: {
    name: 'notification-queue',
    defaultJobOptions: {
      attempts: 2,
      backoff: {
        type: 'fixed' as const,
        delay: 5000,
      },
      removeOnComplete: {
        age: 3600, // 1 hour
        count: 500,
      },
    } satisfies DefaultJobOptions,
  },
};
 
/**
 * Rate limiting configuration (per minute)
 */
export const rateLimits = {
  email: {
    max: 100, // Max 100 emails per duration
    duration: 60000, // Per minute
  },
  notification: {
    max: 200,
    duration: 60000,
  },
};
 
/**
 * Worker concurrency settings
 */
export const workerConcurrency = {
  email: parseInt(process.env.EMAIL_WORKER_CONCURRENCY || '5', 10),
  notification: parseInt(process.env.NOTIFICATION_WORKER_CONCURRENCY || '10', 10),
};