Skip to content

Commit 5194f5d

Browse files
committed
refactor: split queuing query into two
Rather than using an OR which may/may not use the index correctly, we use two queries.
1 parent 84365dd commit 5194f5d

1 file changed

Lines changed: 21 additions & 15 deletions

File tree

apps/backend/src/lib/email-queue-step.tsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -502,15 +502,26 @@ async function renderTenancyEmails(workerId: string, tenancyId: string, group: E
502502
}
503503

504504
async function queueReadyEmails(): Promise<{ queuedCount: number }> {
505-
// Queue emails that are ready to send:
506-
// - Fresh emails: scheduledAt has passed and no retry pending
507-
// - Retry emails: both scheduledAt AND nextSendRetryAt have passed
508-
//
505+
// Queue emails that are ready to send. Split into two queries for clarity and index usage.
509506
// We always require scheduledAt <= NOW() to respect the original scheduling intent.
510-
// nextSendRetryAt should not bypass scheduledAt (e.g., if data is corrupted or manually edited).
511-
//
507+
508+
// Query 1: Fresh emails (scheduledAt has passed, no retry pending)
509+
const freshEmails = await globalPrismaClient.$queryRaw<{ id: string }[]>`
510+
UPDATE "EmailOutbox"
511+
SET "isQueued" = TRUE
512+
WHERE "isQueued" = FALSE
513+
AND "isPaused" = FALSE
514+
AND "skippedReason" IS NULL
515+
AND "finishedRenderingAt" IS NOT NULL
516+
AND "renderedHtml" IS NOT NULL
517+
AND "scheduledAt" <= NOW()
518+
AND "nextSendRetryAt" IS NULL
519+
RETURNING "id";
520+
`;
521+
522+
// Query 2: Retry emails (both scheduledAt AND nextSendRetryAt have passed)
512523
// Clear nextSendRetryAt when queuing so the email is in a clean "queued" state.
513-
const res = await globalPrismaClient.$queryRaw<{ id: string }[]>`
524+
const retryEmails = await globalPrismaClient.$queryRaw<{ id: string }[]>`
514525
UPDATE "EmailOutbox"
515526
SET "isQueued" = TRUE, "nextSendRetryAt" = NULL
516527
WHERE "isQueued" = FALSE
@@ -519,17 +530,12 @@ async function queueReadyEmails(): Promise<{ queuedCount: number }> {
519530
AND "finishedRenderingAt" IS NOT NULL
520531
AND "renderedHtml" IS NOT NULL
521532
AND "scheduledAt" <= NOW()
522-
AND (
523-
-- Fresh emails: no retry pending
524-
"nextSendRetryAt" IS NULL
525-
OR
526-
-- Retry emails: retry time has passed
527-
"nextSendRetryAt" <= NOW()
528-
)
533+
AND "nextSendRetryAt" <= NOW()
529534
RETURNING "id";
530535
`;
536+
531537
return {
532-
queuedCount: res.length,
538+
queuedCount: freshEmails.length + retryEmails.length,
533539
};
534540
}
535541

0 commit comments

Comments
 (0)