Playbook / General system design / Design a distributed job scheduler / task queue

Design a distributed job scheduler / task queue

Expected question

"Design a distributed job scheduler / task queue. How do you enqueue, prioritize, execute, retry, and observe background work at scale?"

Variant forms

Same design, different framing:

  • "Design a system like Celery/Sidekiq at company scale — millions of jobs/day."
  • "How do you schedule cron jobs across a cluster without double execution?"
  • "Design priority queues where paid customers' jobs preempt free tier."
  • "Our workers keep picking poison messages — architect DLQ and retry with backoff."
  • "Design exactly-once vs at-least-once semantics for payment reconciliation jobs."
  • "How do you shard a task queue when Redis/SQS becomes the bottleneck?"
  • "Design a workflow DAG executor (Airflow-style) with dependency tracking."

Where this actually gets asked

Weakly sourced for company-specific interview attribution: no confirmed Blind/Glassdoor post was found for any of the six companies asking this exact question; generic aggregator lists tag it "asked at Google, Amazon, Microsoft" without individual sourcing behind any one company — treat as an unconfirmed general archetype, not a leaked question. Strong grounding: the real-system grounding: Google's own SRE Book has a dedicated chapter, "Distributed Periodic Scheduling with Cron Service," describing Google's real internal distributed cron system built atop Borg and Paxos-based consensus — a genuine Google primary source. The Borg paper itself (Google Research, republished at EuroSys 2015) is directly relevant background. Use this question as a well-known general distributed-systems archetype, grounded in real, published Google infrastructure, rather than a confirmed company-specific interview prompt.

Executive summary

30-second thesis

I'd ship at-least-once with leases + idempotent jobs — not "exactly-once in the scheduler" — and put the dispatcher behind leader election so a crash doesn't silent-drop the fleet.

2-minute answer

Clarify cron vs one-shot queue, side-effect risk, and priority tiers. Default: enqueue with idempotency key, workers lease with token + visibility timeout sized from p99 duration (H), ack/nack, DLQ after N. Scheduler HA via Raft/etcd-style leader — one active firer, no split-brain double-fire. Poison pills quarantine; infinite retry is an outage. Overdue backlog after outage: priority catch-up, and some jobs should be skipped if stale (job-level policy). Don't design Airflow in 45 minutes unless they ask for DAGs.

Quantitative trade-offs

DecisionTrade-off and reversal evidenceEvidence class
Short vs long leaseShort → duplicates; long → stuck recovery. Reverse when p99 job time drifts past visibility timeout.H
At-least-once + idempotency vs pretend exactly-onceIdempotency is the real guarantee; reverse only with transactional outbox into a single side-effect store.R/H
Leader-elected cron vs multi-active fireLeader avoids double-fire; reverse to sharded calendars when one leader can't scan the schedule volume.H

What I'd ask them

  1. Are job side effects naturally idempotent, or do we need dedup keys in the callee?
  2. Cron-at-scale or mostly async task queue?
  3. What's acceptable duplicate rate vs missed-run rate?

Requirements

Functional

  • Schedule jobs to run at a specific time or on a recurring interval (cron-style), across a fleet of worker machines.
  • Guarantee a scheduled job runs — and, critically, runs exactly once even if the scheduler or a worker fails mid-execution, not zero times (silently dropped) or multiple times (duplicated side effects).
  • Support job priorities and retries with backoff for jobs that fail.

Non-functional

  • The scheduler itself must not be a single point of failure — a scheduler crash shouldn't mean no jobs run until it's manually restarted.
  • Jobs need to be idempotent-safe or the system needs deduplication, since distributed systems generally can't guarantee exactly-once execution without one of these.
  • Scale to a very large number of scheduled jobs (Google's real cron service handles this at enormous internal scale) without the scheduling decision itself becoming a bottleneck.

Core entities

  • Job definition: what to run, the schedule (cron expression or one-time timestamp), retry policy, and priority.
  • Job execution: a specific instance of a job definition firing at a specific time, with a status (pending, running, succeeded, failed).
  • Lease: a time-bounded claim by one worker on one job execution, preventing two workers from picking up the same execution simultaneously.
  • Worker: a machine capable of executing jobs, reporting health/liveness to the scheduler.

API / interface

Auth: service accounts; workers claim with lease tokens.

POST /v1/queues/{queue}/tasks
Idempotency-Key: <uuid>
{"payload":{...},"delay_sec":0,"max_attempts":5,"timeout_sec":60}
→ 201 {"task_id":"task_...","status":"queued"}

POST /v1/queues/{queue}/lease
{"worker_id":"w_...","max_tasks":10,"lease_sec":30}
→ 200 {"tasks":[{"task_id":"...","lease_token":"lt_...","payload":{...}}]}

POST /v1/tasks/{task_id}/ack
{"lease_token":"lt_...","result":{...}} → 200 {"status":"succeeded"}

POST /v1/tasks/{task_id}/nack
{"lease_token":"lt_...","retryable":true,"error":"..."} → 200 {"status":"queued","attempt":2}

GET /v1/queues/{queue}/stats
→ {"queued":1203,"leased":80,"dlq":4,"oldest_age_sec":12}

Staff+ callout: lease/ack/nack is the correctness API — visibility timeouts without lease tokens are insufficient.

Data Flow

Enqueue → lease → worker execute → ack/nack; leases prevent double-processing under failure.

Rendering architecture diagram…

High-level design

Rendering architecture diagram…

Google's published cron pattern: the scheduler itself runs as a leader-elected, consensus-backed cluster (not a single instance) so a scheduler failure triggers leader re-election rather than a scheduling outage — and job execution uses a time-bounded lease so a worker failure mid-execution results in reassignment, not a silently lost job.

Deep dive 1: exactly-once execution — the actual hard guarantee

Distributed systems fundamentally cannot guarantee true exactly-once execution across a network that can partition or a worker that can crash mid-job — the real, practical answer is at-least-once execution plus idempotency. The scheduler leases an execution to a worker for a bounded time; if the worker doesn't report completion before the lease expires, the execution is reassigned to another worker. This means a job could run twice (the original worker was just slow, not dead, and both it and the reassigned worker complete) — so job definitions need to either be naturally idempotent (safe to run twice) or use a dedup key the job's own side-effect system checks before acting.

GuaranteeWhat it actually requiresReal-world approach
At-most-onceAccept some jobs silently never runRarely acceptable for anything that matters
At-least-onceRetry on any doubt about completionThe real, practical default — combined with idempotency
Exactly-once (in effect)At-least-once execution + idempotent job logic or a dedup checkThe correct target — achieved by combining the above, not by the scheduler alone

Trap: claiming the scheduler itself can guarantee exactly-once execution through clever engineering alone — a Staff+ answer names this as fundamentally impossible without idempotency and designs for at-least-once-plus-idempotent explicitly.

Deep dive 2: scheduler high availability via consensus

A single-instance scheduler is a hard single point of failure — if it crashes, no jobs fire until it's replaced. Google's real cron-service design (per the SRE Book) runs the scheduler as a small cluster using Paxos-based consensus for leader election: one instance is the active leader firing jobs, the others stand by ready to take over if the leader fails, with consensus ensuring only one leader is ever active at a time (avoiding a split-brain scenario where two schedulers both believe they're the leader and double-fire jobs).

Deep dive 4: lease tuning and poison pills

Too-short leases → duplicate execution; too-long → stuck recovery. Heartbeat to extend; set visibility timeout from p99 job duration. After N failures, quarantine to a DLQ with alert and a replay policy guarded by idempotency — infinite retries are an outage. In 45 minutes, cover lease/ack/idempotency; don't design a full workflow engine.

What's expected at each level

  • Mid-level: proposes a single scheduler process with a jobs database, without addressing scheduler failover or exactly-once semantics.
  • Senior: identifies the need for scheduler redundancy and a lease-based worker assignment mechanism to handle worker failures.
  • Staff+: explicitly names exactly-once execution as unachievable without idempotency, and designs the at-least-once-plus-idempotent-or-dedup pattern rather than claiming the scheduler alone solves it.
  • Principal: additionally designs the scheduler's own high-availability mechanism (leader election via consensus) explicitly, connecting it to the same split-brain-avoidance principle that makes distributed consensus hard in general, not just asserting "run multiple instances."

Follow-up questions to expect

  • "Two schedulers both think they're leader after a partition?" — That's why you use consensus. Majority-ack leader election exists specifically so you don't double-fire; "we elected a leader" without quorum is cosplay.
  • "Huge overdue backlog after an outage?" — Catch up by priority, not blind chronological replay. Some jobs are wrong to run late — that has to be per-job policy, not a global default.