100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

How to Design a Distributed Cron Scheduler

Learn how to design a distributed cron scheduler with exactly-once firing, leader election, and decoupled job execution.

mediumQ87 of 224 in System Design Est. time: 6 minsLast updated:
Open Code Lab

Expected Interview Answer

A distributed cron scheduler stores job definitions with their schedule in a durable database, uses a leader-elected or lock-based trigger process to fire each job exactly once at its scheduled time even across multiple scheduler instances, and hands off actual execution to a job queue so scheduling and running work stay decoupled.

Job definitions (cron expression, next-run time, target action) live in a durable store, and a scanning process periodically queries for jobs whose next-run time has passed. Running multiple scheduler instances for high availability creates a risk of the same job firing twice, so the design uses either leader election (only one instance actively triggers) or a distributed lock per job (each due job is claimed atomically, e.g., via a database row lock or Redis SETNX) so exactly one instance fires it. Once triggered, the scheduler does not execute the job itself — it enqueues a task onto a job queue/worker pool, keeping the scheduling concern (when) separate from the execution concern (how), so a slow job never blocks the timer loop. After firing, the job’s next-run time is recomputed from its cron expression and persisted, and missed fires (e.g., scheduler was down) are handled per a configurable catch-up or skip policy.

  • Exactly-once firing per due job even with multiple scheduler replicas
  • Decouples “when to run” from “how to run”, so slow jobs do not block the scheduler loop
  • Durable persistence means a scheduler restart does not lose job definitions or schedules
  • Configurable catch-up policy handles missed fires after downtime predictably

AI Mentor Explanation

A distributed cron scheduler is like the master fixture list that tells grounds when each scheduled match should start, checked periodically by the tournament office. If two duty officers both watch the same fixture list, only one is allowed to actually blow the whistle and start a match, using a shared sign-off sheet so the match is never started twice. Starting the match is separate from playing it — the officer just triggers kickoff and the players (workers) take over the actual game. If the office was closed during a scheduled start time due to rain, a policy decides whether that match is rescheduled or skipped entirely.

Step-by-Step Explanation

  1. Step 1

    Persist job definitions

    Cron expression, next-run time and target action are stored durably so restarts do not lose schedule state.

  2. Step 2

    Scan for due jobs

    Each scheduler instance periodically queries for jobs whose next-run time has passed.

  3. Step 3

    Claim exactly once

    Leader election or a per-job distributed lock ensures only one instance fires a given due job, avoiding duplicate triggers.

  4. Step 4

    Enqueue and reschedule

    The triggering instance enqueues execution onto a job queue (not run it inline) and recomputes/persists the next-run time.

What Interviewer Expects

  • Explains why multiple scheduler replicas risk duplicate firing and how it is avoided (leader election or per-job lock)
  • Separates “when to trigger” from “how to execute” by handing off to a job queue
  • Discusses durability of job definitions and next-run time across restarts
  • Addresses missed-fire/catch-up policy after downtime

Common Mistakes

  • Running a single cron instance with no plan for what happens if it goes down
  • Executing the job inline in the scheduler loop instead of handing off to a worker/queue
  • Not addressing duplicate firing risk when scaling scheduler instances horizontally
  • Ignoring what happens to jobs whose scheduled time was missed during downtime

Best Answer (HR Friendly)

A distributed cron scheduler is the system that reliably fires scheduled tasks, like a nightly report, at the right time, even if you run several scheduler instances for reliability. It makes sure only one instance actually triggers each task so nothing runs twice, then hands the real work off to a separate worker so the scheduler stays lightweight and on time.

Code Example

Scan-and-claim loop with a per-job lock (pseudo-code)
def scheduler_tick(db, job_queue):
    due_jobs = db.query_due_jobs(now())

    for job in due_jobs:
        acquired = db.try_acquire_lock(
            key=f"cron-lock:{job.id}:{job.next_run_at}",
            ttl_seconds=60,
        )
        if not acquired:
            continue  # another instance already claimed this fire

        job_queue.enqueue({
            "job_id": job.id,
            "scheduled_for": job.next_run_at,
        })

        job.next_run_at = compute_next_run(job.cron_expression, job.next_run_at)
        db.save(job)

Follow-up Questions

  • How would you handle a job whose scheduled fire time was missed because the whole scheduler cluster was down for an hour?
  • How does leader election differ from a per-job distributed lock for avoiding duplicate fires?
  • What happens if the worker that receives the enqueued task fails to run it — is that still the scheduler’s responsibility?
  • How would you support jobs in different time zones with daylight saving transitions?

MCQ Practice

1. Why do multiple scheduler replicas risk duplicate job execution?

Without leader election or per-job locking, every replica that scans and finds a due job could fire it independently.

2. Why should a cron scheduler enqueue work onto a job queue instead of executing it inline?

Decoupling execution into a queue keeps the scheduling loop fast and responsive regardless of how long individual jobs take.

3. What is a common way to guarantee exactly-once firing of a due job across replicas?

A distributed lock or leader election ensures only a single replica actually triggers a given due job.

Flash Cards

Why is exactly-once firing hard with multiple schedulers?Every replica independently scans for due jobs, so without coordination they could all fire the same job.

How is duplicate firing prevented?Leader election (one active trigger) or a per-job distributed lock claimed atomically.

Why hand off execution to a job queue?So a slow job never blocks the scheduler’s timer loop from checking other due jobs.

What is a catch-up policy for?Deciding whether a missed fire (due to downtime) runs late or is skipped entirely.

1 / 4

Continue Learning