feature/IO-3096-GlobalNotifications - Cleanup and Package bumps

This commit is contained in:
Dave Richer
2025-02-19 12:50:01 -05:00
parent 366f7b9c4a
commit 1384616d66
11 changed files with 415 additions and 198 deletions

View File

@@ -4,30 +4,44 @@ const { sendTaskEmail } = require("../../email/sendemail");
let emailQueue;
let worker;
const loadEmailQueue = async ({ pubClient, logger, redisHelpers }) => {
// Consolidate the same way the App Queue Does.
/**
* Initializes the email queue and worker for sending notifications via email.
*
* @param {Object} options - Configuration options for queue initialization.
* @param {Object} options.pubClient - Redis client instance for queue communication.
* @param {Object} options.logger - Logger instance for logging events and debugging.
* @returns {Queue} The initialized `emailQueue` instance for dispatching emails.
*/
const loadEmailQueue = async ({ pubClient, logger }) => {
// Only initialize if queue doesn't already exist
if (!emailQueue) {
logger.logger.info("Initializing Notifications Email Queue");
// Create queue for email notifications
emailQueue = new Queue("notificationsEmails", {
connection: pubClient,
prefix: "{BULLMQ}",
prefix: "{BULLMQ}", // Namespace prefix for BullMQ in Redis
defaultJobOptions: {
attempts: 3,
attempts: 3, // Retry failed jobs up to 3 times
backoff: {
type: "exponential",
delay: 1000
type: "exponential", // Exponential backoff strategy
delay: 1000 // Initial delay of 1 second
}
}
});
// Initialize the worker during queue setup
// Worker to process jobs from the emailQueue
worker = new Worker(
"notificationsEmails",
async (job) => {
const { subject, body, recipients } = job.data;
logger.logger.debug(`Processing email job ${job.id} for ${recipients.length} recipients`);
const { subject, body, recipient } = job.data;
logger.logger.debug(`Processing email job ${job.id} for recipient ${recipient}`);
// Send email to a single recipient
await sendTaskEmail({
to: recipients.map((r) => r.user),
to: recipient, // Single email address
subject,
type: "text",
text: body
@@ -38,9 +52,9 @@ const loadEmailQueue = async ({ pubClient, logger, redisHelpers }) => {
{
connection: pubClient,
prefix: "{BULLMQ}",
concurrency: 2, // Reduced for multi-node setup; adjust based on load
concurrency: 2, // Process up to 2 jobs concurrently
limiter: {
max: 10, // Max 10 jobs per minute per worker
max: 10, // Maximum of 10 jobs per minute
duration: 60 * 1000 // 1 minute
}
}
@@ -59,7 +73,7 @@ const loadEmailQueue = async ({ pubClient, logger, redisHelpers }) => {
logger.logger.error("Worker error:", { error: err });
});
// Graceful shutdown handling
// Graceful shutdown handler for the worker
const shutdown = async () => {
if (worker) {
logger.logger.info("Closing email queue worker...");
@@ -68,13 +82,19 @@ const loadEmailQueue = async ({ pubClient, logger, redisHelpers }) => {
}
};
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown); // Handle termination signal
process.on("SIGINT", shutdown); // Handle interrupt signal (e.g., Ctrl+C)
}
return emailQueue;
return emailQueue; // Return queue for external use
};
/**
* Retrieves the initialized `emailQueue` instance.
*
* @returns {Queue} The `emailQueue` instance for sending emails.
* @throws {Error} If `emailQueue` is not initialized (i.e., `loadEmailQueue` wasnt called).
*/
const getQueue = () => {
if (!emailQueue) {
throw new Error("Email queue not initialized. Ensure loadEmailQueue is called during bootstrap.");
@@ -82,17 +102,31 @@ const getQueue = () => {
return emailQueue;
};
/**
* Dispatches emails to the `emailQueue` for processing, creating one job per recipient.
*
* @param {Object} options - Options for dispatching emails.
* @param {Array} options.emailsToDispatch - Array of email objects to dispatch.
* @param {Object} options.logger - Logger instance for logging dispatch events.
* @returns {Promise<void>} Resolves when all email jobs are added to the queue.
*/
const dispatchEmailsToQueue = async ({ emailsToDispatch, logger }) => {
const emailQueue = getQueue();
for (const email of emailsToDispatch) {
const { subject, body, recipients } = email;
await emailQueue.add("send-email", {
subject,
body,
recipients
}); // Job options moved to defaultJobOptions in Queue
logger.logger.debug(`Added email to queue: ${subject} for ${recipients.length} recipients`);
// Create an array of jobs, one per recipient
const jobs = recipients.map((recipient) => ({
name: "send-email",
data: {
subject,
body,
recipient: recipient.user // Extract the email address from recipient object
}
}));
// Add all jobs for this email in one operation
await emailQueue.addBulk(jobs);
logger.logger.debug(`Added ${jobs.length} email jobs to queue for subject: ${subject}`);
}
};