Design a Distributed Job Scheduler
A Senior/Staff walkthrough of a job scheduler that places 1M+ jobs a day across 10K machines — balancing pickup latency against fair resource sharing, with leader election, lease-based exactly-once execution, cron triggers, and DAG dependencies. It leans on a durable Postgres job store and first-fit matching, then trades those choices against faster or more optimal alternatives. Follow it top to bottom, or jump to any step.
Requirements
Functional Requirements
- Submit jobs: Users submit tasks with resource requirements (CPU, memory, priority)
- Schedule execution: Assign jobs to available machines based on resources and priority
- Cron-like scheduling: Support recurring jobs (run every hour, daily, etc.)
- Retry on failure: Automatically retry failed jobs with configurable policies
- Job lifecycle: Track job status (queued, running, completed, failed)
- Resource management: Fair allocation across teams/tenants
- Dependencies: Jobs can depend on completion of other jobs (DAG)
Non-Functional Requirements
- Scale: Schedule 1M+ jobs/day across 10K+ machines
- Latency: Job picked up within seconds of submission
- Reliability: No job lost — at-least-once execution guarantee
- Fairness: No single tenant monopolizes resources
Back-of-envelope estimation
API design
High-level architecture
Scheduler Design
Leader-elected scheduler (via ZooKeeper/etcd):
1. Scans job queue for eligible jobs (dependencies met, not over quota)
2. Matches jobs to machines based on: resource availability, locality, priority
3. Assigns job to worker agent on selected machine
4. Worker agent executes job, reports status back
Scheduling algorithm: Multi-level priority queue with fair sharing
- Priority levels: Critical > High > Normal > Low
- Within each level, round-robin across tenants/queues
- Resource quotas per tenant prevent starvation
Deep dives
WHERE STAFF IS WONDeep Dive 1: Exactly-Once Execution
Problem: Scheduler assigns job, but doesn’t get ACK (network issue). Is it running or not?
Solution: Lease-based execution
1. Scheduler assigns job with a lease (heartbeat timeout)
2. Worker must send heartbeat every 30 seconds
3. If no heartbeat for 90 seconds, scheduler considers job failed and reschedules
4. Worker checks lease before continuing (avoids duplicate execution after reschedule)
Deep Dive 2: Cron Scheduling
Implementation: Cron evaluator runs every minute
1. Evaluate all cron expressions against current time
2. For matching jobs, create a new job instance
3. Use distributed lock to ensure only one scheduler instance triggers each cron job
4. Store last-triggered time to handle scheduler restarts
Deep Dive 3: DAG Dependencies
- When Job A completes, check if all of Job C's dependencies are met
- If yes, move Job C to "ready" queue
- Cycle detection on job submission (reject cyclic DAGs)
- If any dependency fails, mark downstream jobs as "blocked" and notify
Bottlenecks & tradeoffs
Monitoring
- Job queue depth by priority: Alert if high-priority queue growing
- Job wait time p99: Alert if > 5 minutes
- Worker heartbeat failures: Detect dead machines
- Scheduler failover time: Should be < 30 seconds
- Job success rate: Alert if < 95%
Rubric — Senior vs Staff
Want more breakdowns like this?
Join free early access for upcoming RAG, LLM eval, agents, and AI infrastructure walkthroughs.