Design a distributed GPU training job scheduler with preemption and checkpointing
Expected question
"Design the scheduler for a shared cluster of ~10K GPUs serving thousands of researchers: jobs from 1 GPU to 4K-GPU multi-week pretraining runs, with priorities, fairness, preemption, and failure recovery."
Variant forms
Interviewers often ask the same design with different framing — recognize the archetype:
- "Design a distributed job queue for 100K+ GPU training jobs with preemption and checkpointing."
- "How do you gang-schedule a 512-GPU job without deadlocking the cluster on partial allocation?"
- "Design topology-aware placement — why does NVLink vs. same-rack vs. cross-rack matter?"
- "A 4K-GPU job has waited 6 hours while small jobs keep backfilling around it — fix it."
- "Design checkpoint-interval policy for a training run with a known hardware failure rate."
- "How do you keep GPU utilization high without starving low-priority teams, or letting them starve high-priority ones?"
Where this actually gets asked
Reported from an xAI backend/infra loop, 2025–26; adjacent to OpenAI/frontier-lab infra staples. Distinct from ../general-system-design/04 Distributed job scheduler / task queue: that entry is the generic CPU-oriented queue. GPUs add gang scheduling, topology-aware placement, preemption economics, and checkpoint-resume — where a generic-queue answer runs out of things to say.
Executive summary
30-second thesis
I'd treat GPU scheduling as fundamentally different from a generic queue on four axes — gang scheduling, topology, preemption cost, and failure rate — and make preemption a min-cost decision over priority/checkpoint-age/gang-size, never a boolean kill.
2-minute answer
A 512-GPU job needs all 512 simultaneously; partial allocation deadlocks the cluster, which kills naive work-stealing queues outright. So: reserve-and-drain for large gangs, with bounded backfill — small short jobs fill the accumulating hole only if their estimated runtime fits before the gang's start estimate. Unbounded backfill starves big jobs; no backfill craters utilization.
Placement is topology-aware: NVLink domain beats same-rack (InfiniBand) beats cross-rack, and placement quality alone can move training throughput 2–5x. Preemption isn't "kill and requeue" — you checkpoint, drain, then reschedule, and that cost has to enter the scheduling decision itself: victim selection minimizes total cost (lowest priority, most-recent checkpoint, smallest gang), on a grace period that checkpoints on signal before a hard kill deadline.
At 4K GPUs, hardware failures are the common case, not the exception — I'd quote the intuition behind the Young/Daly checkpoint-interval formula (checkpoint more often as failure rate rises, less often as checkpoints get more expensive) rather than treat checkpointing as a fixed cron. Fairness uses Dominant Resource Fairness with a preemptible opportunistic tier above quota, which is usually the single policy that does the most for utilization.
What I'd ask them: Elastic jobs (resume at a different world size) or must re-gang identically? Central scheduler acceptable at this scale, or does it need to be sharded? What's an acceptable lost-work bound after a failure?
Requirements
Functional
- Accept jobs from 1 GPU to 4K+ GPUs with declared topology hints, priority, and checkpoint config.
- Gang-schedule multi-GPU jobs; never partially allocate a gang.
- Preempt lower-priority jobs to run higher-priority or larger-gang jobs; checkpoint before kill.
- Detect node/job failure and auto-requeue from the last checkpoint.
Non-functional
- High cluster utilization without starving large jobs or low-priority teams.
- Placement-aware scheduling: prefer NVLink domain > same rack > cross-rack.
- MTBF at 4K GPUs implies component failures every few hours (H) — recovery must be the common path, not an edge case.
- Bounded lost work per failure via checkpoint-interval policy.
Core entities
- Job: resource request (GPU count, topology hint), priority, checkpoint config, owning team.
- Gang: the set of tasks that must be scheduled atomically for one job.
- Reservation: an accumulating set of GPUs held toward a pending gang's start.
- Checkpoint: a versioned, tiered snapshot (host RAM → NVMe → object store) tied to a job.
- Node: GPU inventory, health state (ECC counters, NCCL probe results), cordon status.
API / interface
POST /v1/jobs
{ "gpus": 512, "topology_hint": "nvlink-domain", "priority": "P1",
"checkpoint": {"interval_s": 900, "elastic": false} }
→ 202 { "job_id": "job_...", "state": "queued" }
GET /v1/jobs/{job_id}
→ 200 { "state": "running", "gang_size": 512, "last_checkpoint": "..." }
POST /v1/jobs/{job_id}/preempt
{ "reason": "higher_priority_gang" }
→ 202 { "checkpoint_triggered": true, "grace_period_s": 120 }
Staff+ callout: preemption is a first-class API call with a grace period, not a kill -9 the
scheduler happens to also do.
Data Flow
Rendering architecture diagram…
High-level design
Rendering architecture diagram…
Central scheduler with optimistic concurrency (Omega-style) keeps placement globally coherent at this scale; shard by cluster cell only if a single leader becomes the bottleneck.
Deep dive 1: gang scheduling and backfill
Reserve-and-drain accumulates freed GPUs toward a large gang's reservation while it waits. Unbounded backfill of small jobs into the accumulating hole starves the big gang indefinitely; bounded backfill — only admit small jobs whose estimated runtime fits before the gang's start estimate — keeps utilization high without indefinite starvation. Add a hard start deadline with escalating preemption as the deadline nears, so a 4K-GPU job can't wait forever behind an endless stream of small jobs.
Deep dive 2: preemption policy
Victim selection minimizes total cost: prefer lowest priority, most-recent checkpoint (least lost work), smallest gang. Grace period is checkpoint-on-signal (SIGTERM → checkpoint → exit) with a hard kill deadline behind it. Preempted jobs get a requeue priority boost to prevent starvation/thrash, plus a preemption rate limit per job so one job isn't preempted repeatedly before it can make progress.
Deep dive 3: checkpoint interval and topology
Tiered/async checkpointing — snapshot to host memory in seconds so training resumes immediately, then trickle to NVMe and object store. Interval is an optimization problem: the Young/Daly intuition (checkpoint more often as failure rate rises, less often as checkpoints get more expensive) is worth quoting even without deriving the formula live. Elastic jobs can resume at a different world size; non-elastic jobs must re-gang identically. Topology placement (NVLink > rack > cross-rack) is scored the same way at schedule time, since a bad placement silently taxes throughput 2–5x with no error anywhere to point at.
Deep dive 4: fairness and failure handling
Dominant Resource Fairness across teams with weighted shares; quota is not the same as reservation — allow opportunistic burst above quota at preemptible priority, which is usually the single policy that lifts cluster utilization the most. On failure: health checks at the node agent (ECC error counters, idle-time NCCL probes), fail-fast the whole gang on mid-run failure, requeue from checkpoint, cordon the node. Distinguish job-fault from infra-fault for retry-budget accounting so a bad checkpoint doesn't get infinite infra-fault retries.
What's expected at each level
- Mid-level: priority queue + basic preemption, no gang-scheduling awareness.
- Senior: gang scheduling, checkpoint/resume, aware that topology matters.
- Staff+: bounded backfill with a starvation bound, min-cost preemption victim selection, checkpoint-interval reasoning (even informal Young/Daly intuition).
- Principal: opportunistic preemptible tier as a utilization lever, job-fault vs. infra-fault retry-budget distinction, and a story for testing the scheduler itself (simulation, shadow, chaos).
Follow-up questions to expect
- "A 4K-GPU job has waited 6 hours while backfill keeps running." Reservation with a hard start deadline; backfill only jobs whose estimate fits; escalating preemption as the deadline nears.
- "Checkpoints for a 400B model are 5TB — object store write takes 20 minutes." Tiered async (host-RAM snapshot unblocks training immediately), sharded parallel writes per rank, incremental/differential checkpoints for optimizer state.
- "How do you test the scheduler itself?" Discrete-event simulation against production traces, shadow scheduling, chaos testing (kill nodes mid-gang).
Related
- ../general-system-design/04 Distributed job scheduler / task queue — contrast explicitly: generic queue vs. GPU-aware scheduler
- 08 Fine-tuning/RLHF training pipeline at scale
- ../mlops-llmops/05 ML CI/CD & continuous training
- ../cloud-architecture/01 GPU capacity planning & procurement
- ../cloud-architecture/08 Foundation-model pretraining cluster