From system-design-skills
Designs task scheduling systems: job queues, cron at scale, delayed/recurring tasks, worker pools with leasing, priorities, and exactly-once execution.
How this skill is triggered — by the user, by Claude, or both
Slash command
/system-design-skills:task-schedulingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Decide *when* work runs and *which worker* runs it: fire jobs on a schedule
Decide when work runs and which worker runs it: fire jobs on a schedule
(cron/delayed/recurring), hand each job to exactly one worker via a lease, and
make sure it completes once despite crashes and retries. This sits on top of
messaging-streaming queues — the queue is the transport; this skill adds the
scheduling, leasing, priorities, and task-level idempotency. Getting it wrong
shows up as jobs that never run, run twice (double charge, double email), or
pile up until a worker fleet falls permanently behind.
Work must run later (send a reminder in 24h), on a schedule (nightly
rollups, hourly cron), or repeatedly (poll every 5 min); a slow operation is
already off the request path (→ messaging-streaming) and now needs reliable
allocation to a pool of workers; jobs need priorities (paid before free) or
fairness (no single tenant starves others); or a job must complete exactly
once even though the worker holding it can crash mid-flight.
The caller needs the result inline — that's a synchronous call, not a scheduled
job. A single fire-and-forget async step with no schedule, priority, or
exactly-once need — a plain queue + idempotent consumer (messaging-streaming)
is simpler; don't add a scheduler on top. One periodic job on one box —
OS cron is fine until you have multiple schedulers or need history and
retries. A long-running multi-step saga with rollback — reach for a durable
workflow engine instead of hand-rolling state across jobs. Don't stand up
Airflow/Celery "because we'll have batch jobs eventually" (YAGNI): it's a
stateful control plane to operate and monitor.
back-of-the-envelope for
arrival vs. service rate.)api-design.)Scheduling trigger
Worker allocation
Priority & fairness
| Option | What it solves | What it worsens | Change it when |
|---|---|---|---|
| OS cron / single scheduler | Trivial; zero infra | SPOF — node dies, schedule stops; no retry/history | You need HA or missed-run recovery → distributed scheduler |
| Distributed scheduler (HA cron) | Survives node loss; no double-fire (leader-elected) | Needs leader election (→ consistency-coordination); more moving parts | One box and one job is enough → OS cron |
| Delay queue / timer | Per-job delays without a cron tick; precise-ish timing | Far-future jobs sit in the queue; timer accuracy bounded by poll interval | Delays are uniform/periodic → cron; dependencies exist → DAG |
| Workflow DAG (Airflow-style) | Dependencies, backfill, run history, reruns | Heavy control plane; scheduler latency; overkill for single jobs | Jobs are independent one-shots → plain queue + scheduler |
| Pull (worker leasing) | Self-balancing, elastic, natural back-pressure | At-least-once: lease expiry on a slow job re-runs it (need idempotency) | A job must run on a specific node (data locality) → push |
| Push (dispatcher) | Affinity/locality; central control | Dispatcher is a bottleneck/SPOF; must track worker health | No locality need → pull is simpler |
| Priority queues | Important work runs first | Low-priority starvation under sustained load | Fairness across tenants matters → weighted/fair |
| Weighted / fair scheduling | No tenant starves another | More complex; per-tenant accounting | Only one workload class exists → single queue |
A scheduler can quietly fall behind, or it can amplify an outage by re-dispatching work a struggling fleet can't finish.
resilience-failure).consistency-coordination); idempotent enqueue keyed by
(job, scheduled_time).Monitor: oldest-due-job age (the best lateness signal), queue depth per priority, lease-expiry / redelivery rate, retry and DLQ rate, worker utilization, and per-tenant share.
messaging-streaming) is enough.Do
Don't
Size the worker pool with Little's law: in-flight jobs = arrival rate × average
job duration, so workers ≈ peak arrival × avg seconds-per-job / per-worker
concurrency. Sustained drain must exceed peak arrival or the backlog never
clears. Set the visibility timeout above the p99 job duration (a too-short
timeout is the #1 cause of duplicate runs); set far-future delay storage =
delayed-job rate × max delay × job size. Don't restate the latency/QPS tables —
pull the rates and durations from back-of-the-envelope.
A scheduled task is a contract. Define it, not "a job":
task_id / idempotency key (dedup on retry — key
contract owned by api-design), task_type/version, payload, priority,
scheduled_for (run-not-before), attempt, and a trace_id.dedup_key = (job_name, scheduled_time) so a double-enqueue is a no-op.visibility_timeout; it must ack/delete on success or heartbeat to extend.
Lease expiry → automatic redelivery. Failure after the retry cap → DLQ with
attempt count and last error.Default to the generic recipe above. If the user names a cloud, read
references/providers/<provider>.md for the managed-service mapping,
quotas/limits, and provider-specific trade-offs. If no file exists for that
provider, the generic recipe is the answer.
To visualize the scheduler → queue → leased workers path, or the lease-expiry
redelivery loop, use the in-plugin architecture-diagram skill. Quick inline
sketch: [scheduler] → [delay/priority queue] → workers (lease) ─expire→ requeue ─fail×N→ [DLQ];
main path solid, the expiry/DLQ branches dashed.
messaging-streaming — depends on it: queues are the transport this skill
schedules onto and leases from; it owns delivery guarantees, ordering, and DLQs
and this skill does not reimplement them.resilience-failure — pairs with it for the retry policy: backoff, jitter,
and DLQ-as-containment are tuned there; this skill names them.api-design — depends on its idempotency-key contract, the mechanism that
makes a re-run after lease expiry safe.consistency-coordination — depends on it for leader election (and fencing)
so only one scheduler is active and recurring jobs don't double-fire.back-of-the-envelope — feeds into sizing: arrival rate and job duration set
the worker count and backlog drain.system-design — feeds into the orchestrator's reasoning loop; it routes here
when work must run later, on a schedule, or be reliably allocated to workers.references/deep-dive.md — leasing/visibility-timeout mechanics and
heartbeats, distributed-cron + leader election, delay-queue implementations
(sorted set, timer wheel), priority/fairness algorithms, exactly-once-effect via
dedup, and Celery/Sidekiq/Airflow internals. Read when designing the scheduler in detail.references/providers/{generic,aws,azure,gcp,temporal}.md — service
mappings, decision-changing limits, and pitfalls per environment.npx claudepluginhub proyecto26/system-design-skills --plugin system-design-skillsBuilds production-grade scheduled jobs with cron syntax, overlap prevention, monitoring, and structured logging. Activates when users mention cron jobs, scheduled tasks, or recurring jobs.
Background task processing patterns including Celery, ARQ, and Redis queues. Covers configuration, routing, retry strategies, scheduling, monitoring, and distributed workflows.