feature/IO-3096-GlobalNotifications - Checkpoint, Builders

This commit is contained in:
Dave Richer
2025-02-18 12:57:54 -05:00
parent c214ed1dfb
commit adb15a4748
7 changed files with 149 additions and 138 deletions

View File

@@ -1,40 +1,99 @@
const { Queue, Worker } = require("bullmq");
const { sendTaskEmail } = require("../../email/sendemail");
let emailQueue;
let worker;
const loadEmailQueue = async ({ pubClient, logger, redisHelpers }) => {
if (!emailQueue) {
logger.logger.info("Initializing Notifications Email Queue");
emailQueue = await new Queue("notificationsEmails", { connection: pubClient, prefix: "{BULLMQ}" });
}
emailQueue = new Queue("notificationsEmails", {
connection: pubClient,
prefix: "{BULLMQ}",
defaultJobOptions: {
attempts: 3,
backoff: {
type: "exponential",
delay: 1000
}
}
});
// TODO: Test code for worker
// const worker = new Worker(
// "notificationsEmails",
// async (job) => {
// console.log("Processing job", job.id, "with data", job.data);
// // Simulate some work
// await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds
// console.log("Job processed");
// },
// { connection: pubClient, prefix: "{BULLMQ}" }
// );
//
// worker.on("completed", (job) => {
// console.log(`Job ${job.id} completed!`);
// // Optionally, close the worker after it's done
// worker.close().then(() => {
// console.log("Worker closed");
// });
// });
//
// worker.on("error", (err) => {
// console.error("Error in worker:", err);
// });
// Initialize the worker during queue setup
worker = new Worker(
"notificationsEmails",
async (job) => {
const { subject, body, recipients } = job.data;
logger.logger.debug(`Processing email job ${job.id} for ${recipients.length} recipients`);
await sendTaskEmail({
to: recipients.map((r) => r.user),
subject,
type: "text",
text: body
});
logger.logger.debug(`Email job ${job.id} processed successfully`);
},
{
connection: pubClient,
prefix: "{BULLMQ}",
concurrency: 2, // Reduced for multi-node setup; adjust based on load
limiter: {
max: 10, // Max 10 jobs per minute per worker
duration: 60 * 1000 // 1 minute
}
}
);
// Worker event handlers
worker.on("completed", (job) => {
logger.logger.debug(`Job ${job.id} completed`);
});
worker.on("failed", (job, err) => {
logger.logger.error(`Job ${job.id} failed: ${err.message}`, { error: err });
});
worker.on("error", (err) => {
logger.logger.error("Worker error:", { error: err });
});
// Graceful shutdown handling
const shutdown = async () => {
if (worker) {
logger.logger.info("Closing email queue worker...");
await worker.close();
logger.logger.info("Email queue worker closed");
}
};
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
}
return emailQueue;
};
const getQueue = () => (!emailQueue ? loadEmailQueue : emailQueue);
const getQueue = () => {
if (!emailQueue) {
throw new Error("Email queue not initialized. Ensure loadEmailQueue is called during bootstrap.");
}
return emailQueue;
};
module.exports = getQueue;
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`);
}
};
module.exports = { loadEmailQueue, getQueue, dispatchEmailsToQueue };