PracHub
QuestionsCoachesLearningGuidesInterview Prep
|Home/System Design/Robinhood

Design a distributed job scheduler service

Last updated: Jun 24, 2026

Quick Overview

This question evaluates understanding of distributed systems and microservice architecture, scheduling and timeout semantics, fault tolerance, scalability, and data modeling for job metadata, run history, and logs.

  • easy
  • Robinhood
  • System Design
  • Software Engineer

Design a distributed job scheduler service

Company: Robinhood

Role: Software Engineer

Category: System Design

Difficulty: easy

Interview Round: Technical Screen

Design a **distributed job scheduling system** — a cloud-hosted, microservice-based service that lets clients register tasks to run on a schedule and runs them reliably across a fleet of worker machines. A client creates a **job** through an API. Each job specifies: - A **job identifier** (optional; the system generates one if omitted). - A **schedule** — cron-style, "run once at time $T$", or "every $X$ minutes". - The **task** to run — a script name, a container image, or some executable description. - **Resource requirements** at a high level (e.g. a CPU/memory tier or machine class). - A **timeout** value. - A **timeout handler** — what to do if the run exceeds its timeout (kill, mark failed, retry, trigger another job, etc.). The system must: 1. **Run each job reliably at (or near) its scheduled time** across many worker machines, tolerating machine failures and restarts. 2. **Handle jobs that overrun their timeout** — detect the overrun and apply the configured timeout behavior. 3. **Let clients query past run status** — the status of a specific run (`PENDING`, `RUNNING`, `SUCCESS`, `FAILED`, `TIMED_OUT`, …) and the run history for a job (e.g. the last $N$ runs with timestamps and outcomes). 4. **Let clients query logs** (stdout/stderr or structured logs) for a past run. Design the architecture, data model, scheduling logic, execution path, failure handling, and query APIs. Explain your design step by step, justify each major choice, and call out trade-offs. ```hint Where to start Split a latency-sensitive **control plane** (job CRUD, status/log queries) from a high-volume, asynchronous **data plane** (dispatching and running jobs). Sketch the services first — an API/metadata service, a scheduler, a durable queue, a worker/executor pool, a log path — then trace the data flow of a single job from "client creates it" to "logs are queryable." ``` ```hint The scheduling invariant The scheduler's hard problem is "never miss a run, never double-fire" without distributed consensus per job. Think about denormalizing a `next_run_at` onto each job row so the hot path is one indexed range scan, and making run-creation **idempotent**: insert the run row and advance `next_run_at` in the *same* transaction, with a natural key + `ON CONFLICT DO NOTHING` to absorb retries. ``` ```hint Distributing the scheduler safely To run more than one scheduler instance, consider **partitioning** the job space (hash into $P$ partitions, assign partitions to instances via a coordinator) instead of a single leader — and use `SELECT ... FOR UPDATE SKIP LOCKED` as a belt-and-suspenders guard so a brief ownership overlap can't process the same row twice. ``` ```hint Execution semantics — be honest True exactly-once across enqueue → dispatch → a side-effecting container is impossible without the task's cooperation. Aim for **at-least-once + idempotent tasks**: a durable queue (redelivery), a conditional `PENDING → RUNNING` claim to drop duplicate deliveries, a lease/heartbeat so a watchdog can reap dead workers, and a stable per-run idempotency key handed to the task. ``` ```hint Logs and run history at scale Estimate the bytes. Logs dominate (tens of KB per run × runs/sec) — they belong in **object storage**, never the metadata DB, with a lifecycle/retention rule. Run history grows unbounded, so think about **time-partitioning** the runs table and dropping old partitions. ``` ### Constraints & Assumptions - **Scale:** on the order of **hundreds of thousands** of active scheduled jobs. - **Granularity:** at least **minute-level** scheduling (finer is acceptable if your design supports it). - **Reliability over exact timing:** running a job a few seconds late is acceptable; **skipping a run entirely is a bug**. This priority should drive your delivery-semantics choice. - **Fault tolerance:** if a scheduler instance or worker crashes or restarts, every due job must still eventually run. - **Architecture:** microservice-based, cloud-hosted (containers/VMs), with components that scale independently. - Treat anything not specified above (timezone/DST handling, log retention period, exactly-once vs at-least-once, missed-window catch-up policy, security/multi-tenancy depth) as an assumption you state explicitly. ### Clarifying Questions to Ask - What execution semantics are required — **exactly-once**, or is **at-least-once with idempotent tasks** acceptable? (This is the single biggest fork in the design.) - On a **missed window** after an outage (job's `next_run_at` is now in the past), should we **fire once and skip ahead**, or **backfill every missed slot**? Is this per-job configurable? - How accurate must timing be — is "within a few seconds" of the scheduled time good enough, or are there sub-second SLAs? - What is the expected **task duration distribution** and **peak concurrency**? (This sizes the worker fleet independently of job count.) - What are the **log retention** and **run-history retention** requirements? - How rich are the **resource requirements** — a few coarse tiers, or fine-grained CPU/memory/GPU bin-packing? ### What a Strong Answer Covers - **Requirements & scope discipline:** restates functional/non-functional requirements, states the execution-semantics decision up front, and explicitly sets aside non-goals (DAG orchestration, secrets pipeline, etc.). - **Sizing that drives decisions:** back-of-envelope for runs/sec (including the **bursty minute-boundary** cluster), metadata write volume, run-history growth, and log throughput — used to justify batching, partitioning, and object-store-for-logs. - **Clean architecture:** named services with clear responsibilities, a **control-plane vs data-plane** split, and synchronous (REST/gRPC) vs asynchronous (durable queue) communication choices justified. - **Data model & storage choices:** relational (OLTP) for job + run state with the correctness primitives that motivate it (transactions, unique-key dedup, `SKIP LOCKED`); object storage for logs; a clearly defined **run identity** and the indexes that make the hot scheduling query fast. - **Scheduling correctness:** the tick loop, the "insert run + advance `next_run_at` in one transaction" invariant, distributing the scheduler (partitioning vs single leader), and an explicit argument for **why neither double-scheduling nor missed runs occur**. - **Execution & timeouts:** atomic claim of a run, lease/heartbeat, local timeout enforcement plus the four configurable timeout actions, and log capture. - **Failure handling:** scheduler crash, worker death mid-run, lost queue message, duplicate delivery, DB failover, full restart — each with its recovery path; a **watchdog/reaper** as the self-healing backstop. - **Query APIs:** concrete REST endpoints for job CRUD, run history (with pagination), single-run status, and log fetch (ideally via pre-signed URL). - **Scalability & trade-offs:** how each tier scales, sharding/partitioning of job data, and an honest discussion of the central bottleneck and the at-least-once-vs-exactly-once trade-off. ### Follow-up Questions - How exactly do you guarantee a job is **not double-run**, and where in your design can a duplicate still slip through? Walk through the specific crash/partition windows and what absorbs each. - A **network-partitioned but still-alive** worker keeps running its container while its lease lapses. The watchdog re-enqueues a retry. How do you keep this concurrent double-run from corrupting external state? - The minute-boundary burst (e.g. 10% of jobs fire at the top of the hour) creates a **thundering herd**. How do you keep that from overwhelming the metadata DB and the worker fleet? - How would you support **per-job resource requirements** without building a full bin-packing scheduler? When would coarse tiers stop being enough? - How would you extend this to **job dependencies / DAGs** (run B only after A succeeds), and why is that a meaningfully different system?

Quick Answer: This question evaluates understanding of distributed systems and microservice architecture, scheduling and timeout semantics, fault tolerance, scalability, and data modeling for job metadata, run history, and logs.

Related Interview Questions

  • Design a Photo Album App - Robinhood (medium)
  • Design a distributed job scheduler - Robinhood (medium)
  • Design a Photo Management Service - Robinhood (medium)
  • Design a job scheduler with SLA and logs - Robinhood (medium)
  • Design authorization and audit logging systems - Robinhood (medium)
|Home/System Design/Robinhood

Design a distributed job scheduler service

Robinhood logo
Robinhood
Dec 8, 2025, 7:45 PM
easySoftware EngineerTechnical ScreenSystem Design
44
0

Design a distributed job scheduling system — a cloud-hosted, microservice-based service that lets clients register tasks to run on a schedule and runs them reliably across a fleet of worker machines.

A client creates a job through an API. Each job specifies:

  • A job identifier (optional; the system generates one if omitted).
  • A schedule — cron-style, "run once at time TTT ", or "every XXX minutes".
  • The task to run — a script name, a container image, or some executable description.
  • Resource requirements at a high level (e.g. a CPU/memory tier or machine class).
  • A timeout value.
  • A timeout handler — what to do if the run exceeds its timeout (kill, mark failed, retry, trigger another job, etc.).

The system must:

  1. Run each job reliably at (or near) its scheduled time across many worker machines, tolerating machine failures and restarts.
  2. Handle jobs that overrun their timeout — detect the overrun and apply the configured timeout behavior.
  3. Let clients query past run status — the status of a specific run ( PENDING , RUNNING , SUCCESS , FAILED , TIMED_OUT , …) and the run history for a job (e.g. the last NNN runs with timestamps and outcomes).
  4. Let clients query logs (stdout/stderr or structured logs) for a past run.

Design the architecture, data model, scheduling logic, execution path, failure handling, and query APIs. Explain your design step by step, justify each major choice, and call out trade-offs.

Constraints & Assumptions

  • Scale: on the order of hundreds of thousands of active scheduled jobs.
  • Granularity: at least minute-level scheduling (finer is acceptable if your design supports it).
  • Reliability over exact timing: running a job a few seconds late is acceptable; skipping a run entirely is a bug . This priority should drive your delivery-semantics choice.
  • Fault tolerance: if a scheduler instance or worker crashes or restarts, every due job must still eventually run.
  • Architecture: microservice-based, cloud-hosted (containers/VMs), with components that scale independently.
  • Treat anything not specified above (timezone/DST handling, log retention period, exactly-once vs at-least-once, missed-window catch-up policy, security/multi-tenancy depth) as an assumption you state explicitly.

Clarifying Questions to Ask

  • What execution semantics are required — exactly-once , or is at-least-once with idempotent tasks acceptable? (This is the single biggest fork in the design.)
  • On a missed window after an outage (job's next_run_at is now in the past), should we fire once and skip ahead , or backfill every missed slot ? Is this per-job configurable?
  • How accurate must timing be — is "within a few seconds" of the scheduled time good enough, or are there sub-second SLAs?
  • What is the expected task duration distribution and peak concurrency ? (This sizes the worker fleet independently of job count.)
  • What are the log retention and run-history retention requirements?
  • How rich are the resource requirements — a few coarse tiers, or fine-grained CPU/memory/GPU bin-packing?

What a Strong Answer Covers

  • Requirements & scope discipline: restates functional/non-functional requirements, states the execution-semantics decision up front, and explicitly sets aside non-goals (DAG orchestration, secrets pipeline, etc.).
  • Sizing that drives decisions: back-of-envelope for runs/sec (including the bursty minute-boundary cluster), metadata write volume, run-history growth, and log throughput — used to justify batching, partitioning, and object-store-for-logs.
  • Clean architecture: named services with clear responsibilities, a control-plane vs data-plane split, and synchronous (REST/gRPC) vs asynchronous (durable queue) communication choices justified.
  • Data model & storage choices: relational (OLTP) for job + run state with the correctness primitives that motivate it (transactions, unique-key dedup, SKIP LOCKED ); object storage for logs; a clearly defined run identity and the indexes that make the hot scheduling query fast.
  • Scheduling correctness: the tick loop, the "insert run + advance next_run_at in one transaction" invariant, distributing the scheduler (partitioning vs single leader), and an explicit argument for why neither double-scheduling nor missed runs occur .
  • Execution & timeouts: atomic claim of a run, lease/heartbeat, local timeout enforcement plus the four configurable timeout actions, and log capture.
  • Failure handling: scheduler crash, worker death mid-run, lost queue message, duplicate delivery, DB failover, full restart — each with its recovery path; a watchdog/reaper as the self-healing backstop.
  • Query APIs: concrete REST endpoints for job CRUD, run history (with pagination), single-run status, and log fetch (ideally via pre-signed URL).
  • Scalability & trade-offs: how each tier scales, sharding/partitioning of job data, and an honest discussion of the central bottleneck and the at-least-once-vs-exactly-once trade-off.

Follow-up Questions

  • How exactly do you guarantee a job is not double-run , and where in your design can a duplicate still slip through? Walk through the specific crash/partition windows and what absorbs each.
  • A network-partitioned but still-alive worker keeps running its container while its lease lapses. The watchdog re-enqueues a retry. How do you keep this concurrent double-run from corrupting external state?
  • The minute-boundary burst (e.g. 10% of jobs fire at the top of the hour) creates a thundering herd . How do you keep that from overwhelming the metadata DB and the worker fleet?
  • How would you support per-job resource requirements without building a full bin-packing scheduler? When would coarse tiers stop being enough?
  • How would you extend this to job dependencies / DAGs (run B only after A succeeds), and why is that a meaningfully different system?

Submit Your Answer to Earn 20XP

Sign in to leave a comment

Loading comments...

Browse More Questions

More System Design•More Robinhood•More Software Engineer•Robinhood Software Engineer•Robinhood System Design•Software Engineer System Design

Your design canvas — auto-saved

PracHub

Master your tech interviews with 8,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.