Case study: Distributed job scheduler
Design a fault-tolerant distributed job scheduler with sharded time buckets, worker lease heartbeats, fencing tokens, and 00:00 UTC spike control.
35 min9 steps
By the end you can
- Estimate worker concurrency using Little's law, schedule storage, and the 00:00 UTC top-of-the-hour cron spike
- Design a two-tier storage and ready-queue architecture that finds due jobs without full-table scans
- Use lease heartbeats, visibility timeouts, and monotonic fencing tokens (`leaseEpoch`) to prevent duplicate side effects from zombie workers
- Predict how the scheduler handles worker crashes, midnight cron surges, poison-pill jobs, and database leader failover
Pin down what 'reliable scheduling' means
Features
- Delayed/cron jobs; cancel, inspect, history, retries, timeouts, DLQ
Targets
- Dispatch within about 1 s; survive midnight bursts
Safety
- At-least-once dispatch; downstream fencing + execution idempotency
Size the worker pool and the 00:00 UTC herd
Executions
5.8k/s
500M/day / 86,400
Busy slots
2,900
5,800/s * 0.5 s; add headroom
Midnight burst
100k jobs/s
Illustrative 17x burst; specify schedule distribution
Job metadata
25 GB
50M * 500 B
History
700 GB/week
3.5B * 200 B
Design the submission and lease heartbeat APIs
Submit
POST /v1/jobs + Idempotency-Key; schedule, payload, timeoutSec, maxAttempts
Inspect
GET /v1/jobs/{id}/executions; status + leaseEpoch
Heartbeat
POST /v1/executions/{id}/heartbeat; workerId + epoch
Complete
POST /v1/executions/{id}/complete; stale epoch -> 409
Model sharded time buckets and execution state
Jobs
hash(jobId)%64; index bucketId, status, nextRunAt
Execution ID
jobId + scheduledAtEpoch; unique row prevents duplicate creation
Lease
attempt, leaseEpoch, leaseUntil; ENQUEUED/RUNNING/SUCCEEDED/FAILED/DEAD_LETTER
Connect the durable schedule store to the ready queue
Order the steps that turn a due cron job in the database into a completed execution.
- 1The scheduler owning bucket b queries the composite index `(bucketId, status, nextRunAt <= now)`
- 2A worker pulls the message, claims `leaseEpoch`, runs the job with heartbeats, and commits `status = SUCCEEDED` with `WHERE lease_epoch = ?`
- 3In one transaction, insert the deterministic execution row for this tick and advance `jobs.nextRunAt` to the next cron time
- 4Publish the ready execution message to the partitioned ready queue
Deep dive: fencing tokens and the 00:00 UTC herd
Lease 1
- Worker freezes 75 s; lease expires at 60 s
Lease 2
- Reassign epoch 2; downstream storage and DB enforce token
Zombie
- Epoch 1 resumes; reject stale writes
Midnight
- Hash-jitter where allowed; separate HIGH and BATCH lanes
Compare the alternatives
scheduler-no-fencing
- Crash/replay causes duplicateEffect
scheduler-table-scan
- Unindexed polling; goodput <70%
Timing wheel
- O(1) RAM timers; durable loader fetches next 60 s
Run the failure scenarios
Inject
Workers down 1.3-1.8 s
Observe
Replay after 400 ms visibility timeout
Recover
execdedup prevents duplicate effects
Inject
Traffic x3
Observe
readyqueue buffers burst
Recover
Steady worker rate; >99% goodput
Inject
DB leader dies at 1.5 s
Observe
Cached status reads continue
Recover
About 0.5 s promotion; fence old leader
Wrap up and self-assess
Lag
startedAt - scheduledAt; real-time p99 <1 s
Failure
Alert tenants on DLQ entries
Lease
Track 409 Stale Lease; compare runtime against heartbeat window
Answer this step to continue.