Skip to content

Commit a1aa168

Browse files
improvement(schedules): jitter scheduled execution starts by 0-30s (#4750)
Cron schedules all fire on the same boundary (e.g. every :00), stampeding the Postgres connection pool at the top of each minute/hour. Spread each due schedule's start across a [0, 30s) window via trigger.dev's delay option (no compute billed during the delay). Wires the previously-unused EnqueueOptions.delayMs through the trigger.dev backend.
1 parent 0fedceb commit a1aa168

2 files changed

Lines changed: 12 additions & 0 deletions

File tree

apps/sim/app/api/schedules/execute/route.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ const JOB_CHUNK_SIZE = 100
2626
const MAX_TICK_DURATION_MS = 3 * 60 * 1000
2727
const STALE_SCHEDULE_CLAIM_MS = getMaxExecutionTimeout()
2828

29+
/**
30+
* Upper bound (ms) for the random start delay applied to each scheduled
31+
* execution. Cron schedules all fire on the same boundary (e.g. every `:00`),
32+
* which stampedes the database connection pool at the top of each minute/hour.
33+
* Spreading starts across a [0, 30s) window smooths that burst.
34+
*/
35+
const SCHEDULE_JITTER_MAX_MS = 30_000
36+
2937
const dueFilter = (queuedAt: Date) =>
3038
and(
3139
isNull(workflowSchedule.archivedAt),
@@ -217,6 +225,7 @@ async function processScheduleItem(
217225
const jobId = await jobQueue.enqueue('schedule-execution', payload, {
218226
jobId: scheduleJobId,
219227
concurrencyKey: scheduleJobId,
228+
delayMs: Math.floor(Math.random() * SCHEDULE_JITTER_MAX_MS),
220229
metadata: {
221230
workflowId: schedule.workflowId ?? undefined,
222231
workspaceId: resolvedWorkspaceId ?? undefined,

apps/sim/lib/core/async-jobs/backends/trigger-dev.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ export class TriggerDevJobQueue implements JobQueueBackend {
8181
triggerOptions.idempotencyKey = options.jobId
8282
triggerOptions.idempotencyKeyTTL = '14d'
8383
}
84+
if (options?.delayMs && options.delayMs > 0) {
85+
triggerOptions.delay = new Date(Date.now() + options.delayMs)
86+
}
8487
const handle = await tasks.trigger(taskId, enrichedPayload, triggerOptions)
8588

8689
logger.debug('Enqueued job via trigger.dev', { jobId: handle.id, type, taskId, tags })

0 commit comments

Comments
 (0)