System Design blueprint · Fintech & Banking
Distributed job scheduler
Cron at scale: due jobs are claimed by one leader, queued by priority and run by idempotent workers.
- Steps
- 6
- Subjective questions
- 32
- Objective questions
- 32
The problem and the outcome
Problem: Statement runs, interest accruals and payment reminders must fire on time across many servers, exactly once in effect.
Outcome: Millions of scheduled jobs a day, none lost and none applied twice.
Architecture
How it works, step by step
- Teams register jobs through an API: a cron expression or a one-off run-at time, plus a payload.
- Each job is a row with its next_run_at, indexed so “what is due now” is one cheap query.
- One leader polls for due jobs; a lease in ZooKeeper, etcd or Redis makes sure only one scheduler leads at a time.
- Due jobs go onto a queue by priority, and next_run_at moves forward in the same transaction.
- Workers must be idempotent: at-least-once delivery means a job can run twice.
- Failures retry with exponential backoff; after the last attempt they land in a dead-letter queue for a person.
Tech stack
- PostgreSQL jobs table (FOR UPDATE SKIP LOCKED)
- Leader election with etcd, ZooKeeper or Redis
- Kafka / SQS work queue
- Worker pool on Kubernetes
- Dead-letter queue
Key design decisions
- Store next_run_at and index it, so finding due jobs is one cheap range query.
- Only one leader schedules at a time, via a lease, so a job is not enqueued twice by two schedulers.
- Idempotent jobs with an execution ID, because at-least-once delivery means a job can run twice.
Common mistakes
- Running cron on every server, so each job runs once per machine.
- Holding a database lock for the whole job instead of just while claiming it.
- Retrying instantly in a tight loop and overloading the system that is already failing.
What to measure
- Schedule delay (actual start minus planned start)
- Job success rate and retries
- DLQ size
- Queue depth by priority
Most-asked interview questions
Subjective questions 32
1How do you make sure a scheduled job runs only once across many servers?Asked almost every time
Elect one leader with a time-limited lease (etcd, ZooKeeper, or a Redis lock with a fencing token) to claim due jobs. Or let every worker claim rows with SELECT … FOR UPDATE SKIP LOCKED, which gives each due job to exactly one claimer. Either way, make the job idempotent, because a crash after the work but before the acknowledgement will cause a retry.
2Walk through the architecture of a distributed job scheduler.Asked almost every time
Clients register jobs (one-off or cron) through an API that stores definitions and next run times in a database. A scheduler component, with a single active leader or partitioned ownership, finds due jobs and enqueues them. Worker pools pull from queues, execute with timeouts, report status and heartbeats, and failed runs are retried or sent to a dead-letter queue.
3How do you find due jobs efficiently?Asked almost every time
Index jobs by next_run_at and poll in small batches (WHERE next_run_at <= now() ORDER BY next_run_at LIMIT n FOR UPDATE SKIP LOCKED), or use a timing wheel or Redis sorted set scored by run time. After claiming, compute and store the next run time for recurring jobs.
4How do you guarantee at-least-once execution and handle duplicates?Asked almost every time
Jobs are only marked done after the worker reports success; a lease with a timeout returns stuck jobs to the queue. Because a job might run twice, make handlers idempotent with job run IDs and idempotency keys for external effects.
5How would you retry failed jobs safely?Very often asked
Retry with exponential backoff and jitter, cap the number of attempts, and move the job to a dead-letter queue after the last one. Separate retryable errors (timeouts) from permanent ones (bad input), which should go straight to the DLQ.
6What is a lease or visibility timeout?Very often asked
When a worker claims a job, it gets exclusive ownership for a period; it must finish or extend the lease with heartbeats. If the worker dies, the lease expires and another worker can pick the job up.
7How do you handle long-running jobs?Very often asked
Break them into smaller steps with checkpoints, send heartbeats to extend leases, store progress so retries resume rather than restart, and give them a separate queue and worker pool so they do not block short jobs.
8How do you schedule millions of jobs at the same time, such as midnight?Very often asked
Spread load with jitter where exact time is not required, partition jobs across scheduler shards, enqueue in batches ahead of time, and scale workers predictively before known peaks.
9How do you support job priorities?Very often asked
Use separate queues per priority with weighted consumption, or a priority field in the queue, and reserve capacity for high-priority jobs so a flood of low-priority work cannot starve them.
10How do you handle time zones and daylight saving in cron schedules?Very often asked
Store each schedule with its IANA time zone, compute next run times with a proper library, and define behaviour for skipped or repeated local times during DST changes, documenting it for users.
11How do you model job dependencies (DAGs)?Very often asked
Store jobs as nodes with edges, run a job only when all its parents succeed, track run state per DAG execution, and support retries per node; tools like Airflow, Temporal or Step Functions implement this.
12How do you expose job status to users?Very often asked
Record each run with state (queued, running, succeeded, failed, cancelled), timestamps, attempts, logs and output references, and provide an API and UI to list runs, retry, cancel or pause schedules.
13How do you guarantee a job runs exactly once in effect, even though delivery is at-least-once?Very often asked
Combine at-least-once delivery with an idempotent handler: give each run a unique run ID, record completion in the same transaction as the job's side effect or check a dedupe table before acting, and use idempotency keys when calling external APIs so a retry after a crash does nothing new.
14How would you design a delayed-job queue, such as 'send this email in 3 days'?Very often asked
Store jobs with a due time in a table indexed by due time, or in a Redis sorted set scored by timestamp. A poller moves due jobs into the ready queue in small batches with SKIP LOCKED or an atomic pop, so millions of future jobs cost nothing until they are due.
15What is a fencing token and why does it matter for leader election?Often asked
It is a number that increases with every new lease. If an old leader pauses (say, a long GC pause) and wakes up after its lease expired, its writes carry an old token and the storage rejects them, so two leaders cannot both act.
16How do you cancel a running job?Often asked
Mark the run cancelled in the store; workers check a cancellation flag periodically or receive a signal, stop work at a safe point and clean up. Jobs that cannot stop safely should finish and ignore their results.
17What is the difference between a scheduler and a workflow engine?Often asked
A scheduler triggers jobs at times or intervals. A workflow engine (Temporal, Step Functions) orchestrates multi-step processes with state, retries, waits and compensation, often triggered by a scheduler.
18How do you avoid overloading downstream systems with jobs?Often asked
Apply concurrency limits per job type or tenant, rate limits for external APIs, and backpressure from queue depth; spread retries with jitter.
19How do you make the scheduler highly available?Often asked
Run several scheduler instances with leader election (etcd, ZooKeeper or database locks with fencing tokens) or partition jobs so each instance owns a shard; workers are stateless and scale horizontally.
20How should failures be retried?Often asked
Retry transient failures with exponential backoff and jitter up to a maximum, classify permanent errors to fail fast, and move exhausted jobs to a dead-letter queue with alerts.
21How do you support multi-tenant fairness?Often asked
Track usage per tenant, apply per-tenant concurrency and rate limits, and use fair queuing so one tenant's burst does not delay everyone else's jobs.
22How do you test a job scheduler?Often asked
Use a fake clock to test due-time calculation and DST edge cases, simulate worker crashes to verify lease expiry and retries, and load-test bursts of due jobs.
23How do you store job logs and outputs?Often asked
Stream logs to a central logging system tagged with job and run IDs, store large outputs in object storage, and keep links in the run record with retention policies.
24How do you handle a job that keeps failing because of bad input?Often asked
Classify the error as permanent, stop retrying, move the job to a dead-letter queue with its payload, error and attempt history, alert the owning team, and provide a tool to fix and replay it once the input or code is corrected.
25How do you let users pause and resume a recurring schedule?Often asked
Store a status on the schedule definition, skip creating new runs while paused, decide whether missed runs should be skipped or caught up on resume, and show the next run time so the behaviour is clear in the UI.
26How do you run a job across many items, such as one million user records?Often asked
Split it into chunks, enqueue a child job per chunk, track progress in a parent record, cap concurrency so the database is not overwhelmed, make each chunk idempotent, and finish with a completion job once all children succeed.
27How do you prevent two scheduler instances from creating the same cron run?Often asked
Elect one leader with a lease in a coordination store, or let every instance try to insert the run with a unique key on schedule ID plus scheduled time so only one insert succeeds and the rest are ignored.
28Which existing tools would you consider instead of building one?Sometimes asked
Cloud schedulers (EventBridge Scheduler, Cloud Scheduler), queue-based libraries (BullMQ, Sidekiq, Celery), workflow engines (Temporal, Airflow, Inngest), and Kubernetes CronJobs, depending on scale and needs.
29What metrics matter for a scheduler?Sometimes asked
Schedule lag (actual start minus due time), queue depth, run duration, success and failure rates, retries, dead-letter counts and worker utilisation.
30How do you handle a job that was due while the scheduler was down?Sometimes asked
Decide a misfire policy per job: run once immediately, run every missed occurrence, or skip to the next schedule, and record the choice.
31How do you version job code safely?Sometimes asked
Keep job payloads backward compatible, version handlers, drain old versions before removing them, and avoid changing the meaning of queued jobs.
32How do you estimate worker capacity for a job system?Sometimes asked
Multiply jobs per second by average runtime to get the concurrent jobs needed, add headroom for peaks and retries, check per-worker memory and CPU, and auto-scale workers on queue depth and job age rather than CPU alone.
Objective questions 32
1What does SELECT … FOR UPDATE SKIP LOCKED let workers do?Asked almost every time
Why: Rows another transaction has locked are skipped, so workers grab different jobs in parallel.
2What returns a stuck job to the queue when a worker dies?Asked almost every time
Why: Another worker can then claim it.
3Why must job handlers be idempotent?Asked almost every time
Why: Duplicates must be harmless.
4Which query lets workers claim due jobs without blocking each other?Asked almost every time
Why: Locked rows are skipped.
5Why add jitter to retry backoff?Very often asked
Why: Randomness spreads retries out, avoiding a thundering herd against the recovering service.
6How do long jobs keep ownership?Very often asked
Why: No heartbeat means the lease expires.
7How are daylight-saving issues handled?Very often asked
Why: Skipped or repeated hours need rules.
8What prevents low-priority floods from starving urgent jobs?Very often asked
Why: This isolates priorities from each other.
9Which tool orchestrates multi-step workflows with state?Very often asked
Why: Workflow engines handle retries and waits.
10What is a misfire policy?Very often asked
Why: Run once, run all or skip.
11Where should exhausted jobs go?Very often asked
Why: Investigate and replay later.
12Exactly-once effect is usually achieved with…Very often asked
Why: Duplicates are expected; the handler makes them harmless.
13Which structure suits millions of future-dated jobs in Redis?Very often asked
Why: Due jobs are fetched by score range cheaply.
14Running crontab on every app server causes…Often asked
Why: Each server's cron fires independently, so a job meant to run once runs N times.
15Which metric shows the scheduler is falling behind?Often asked
Why: Actual start minus due time.
16How do you spread a midnight spike?Often asked
Why: Jitter smooths the load.
17What protects downstream APIs from job floods?Often asked
Why: Respect their capacity.
18How should a running job be cancelled?Often asked
Why: Stop at a safe point.
19What ensures fairness between tenants?Often asked
Why: One burst should not delay all.
20Which component runs a job only after its parents succeed?Often asked
Why: Dependencies form a graph.
21What makes the scheduler itself highly available?Often asked
Why: Avoid single points of failure.
22What should a fake clock help test?Often asked
Why: Time logic is tricky.
23A job failing on bad input should be…Often asked
Why: Permanent errors need a person, not more retries.
24A unique key on schedule ID plus scheduled time prevents…Often asked
Why: Only one insert for each slot can succeed.
25A million-item job is best processed as…Often asked
Why: Chunks can retry and scale independently.
26Workers should usually auto-scale on…Often asked
Why: These show work waiting, not just busy CPUs.
27Which AWS service schedules invocations without servers?Sometimes asked
Why: Managed cron and one-off schedules.
28Where should large job outputs live?Sometimes asked
Why: Queue messages should stay small.
29Changing the meaning of queued job payloads risks…Sometimes asked
Why: Keep payloads compatible.
30Permanent errors should be…Sometimes asked
Why: Retries cannot fix them.
31Kubernetes runs scheduled pods with…Sometimes asked
Why: Built-in cron for clusters.
32When a paused schedule resumes, you must decide…Sometimes asked
Why: Both are valid; users need to know which applies.
Track your progress through every blueprint in your dashboard.
Start learning