Design a Durable Task Scheduling Service
Scale AIBackend Engineer · OnsiteLast updated
Design a resilient, distributed task scheduling service that decouples scheduling from execution, supporting both delayed one-time tasks and recurring cron jobs with strong durability and at-least-once delivery.
Scenario
Many machine learning and data labeling workflows require scheduling actions in the future: triggering hourly model evaluation benchmarks, kicking off overnight data sync pipelines, or retrying failed webhook deliveries with exponential backoff.
Design a multi-tenant, durable distributed task scheduling service that allows client systems to schedule, cancel, and monitor delayed one-time tasks and recurring (cron) tasks, while strictly decoupling the scheduling coordination from actual job execution.
Requirements
Functional Requirements
- Schedule Tasks: Support one-time tasks scheduled at an absolute timestamp (
run_at) and recurring tasks defined by a cron expression (schedule). - Decoupled Execution: The scheduler should be responsible only for initiating the task execution (e.g., dispatching an event to a message queue or invoking an execution worker), not for the task computation itself.
- Cancellation & Updates: Allow callers to cancel or reschedule pending tasks before their execution begins.
- Execution Guarantees: Ensure at-least-once dispatch semantics. Downstream consumers should be provided with mechanisms (like idempotency keys) to prevent duplicate runs.
- Retry & Recovery: If task dispatch fails or an executor crashes mid-run, support configurable retries with backoff and eventual dead-letter routing.
Non-Functional Requirements
- Scale: Maintain up to 50 million pending tasks at any time; sustain a peak dispatch rate of 10,000 tasks/second.
- Accuracy & Latency: Tasks should be dispatched within 1 second of their scheduled trigger time.
- Durability: Zero task loss once acknowledged. State must persist across node crashes and cluster re-elections.
- Fault Tolerance: Gracefully handle clock drift, node failures, and high concurrency on identical execution targets.
What Your Design Should Cover
- API & Data Model: Define endpoints for scheduling, cancellation, and status lookups. Design schema for task definitions, schedules, and run history.
- Time-Wheel / Due-Work Claiming: How due tasks are discovered, partitioned, and claimed across multiple scheduler nodes without contention or duplicate dispatches.
- Decoupled Dispatch: How the scheduler hands off work to execution runtimes (pull vs. push, message queues, leases).
- Failure Modes & Edge Cases: Handling thundering herds (e.g., 100k tasks scheduled for midnight), missed runs during scheduler downtime, and recurring task catch-up policies.
Hints
Hint 1
Separate storage into a persistent state store (for scheduling metadata and recurring configurations) and a fast, ephemeral dispatch queue (e.g., SQS or Kafka) for immediately runnable tasks.
Hint 2
For claiming due tasks from a relational or document store, consider partitioning tasks by time buckets or shards, and using row-level locking patterns like `SKIP LOCKED` or distributed leases.
Hint 3
Decide explicitly how to handle missed recurring executions after extended scheduler downtime—whether to run every missed window ('catch-up') or advance to the next scheduled interval.
Follow-up questions
- How would you address clock drift across scheduling nodes to prevent premature execution?
- If a single tenant schedules 500,000 tasks for the exact same second, how do you prevent storage hotspots and dispatch queue starvation for other tenants?
- How would you build an audit and verification pipeline to guarantee and alert that no scheduled task was dropped or indefinitely stuck?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.