Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture

 ┌──────────────── cronoq (Rust API) · cronoq (Python) · cronoq-server (gRPC/HTTP) ───────────────┐
 │                                                                                               │
 │  Scheduler handle ──commands (bounded mpsc, batched)──▶ ┌──────────────── driver task ───────┐  │
 │  TaskHandle ◀──────oneshot results / broadcast events── │  owns   cronoq-core::Core (sans-IO)│  │
 │                                                         │  sleep_until(core.next_wakeup())   │  │
 │  executors ──feedback (completions, heartbeats)───────▶ │  executes Actions:                 │  │
 │                                                         │   Dispatch → spawn executor        │  │
 │                                                         │   Abort    → cancel attempt        │  │
 │                                                         │   ScaleTo  → Scaler                │  │
 │                                                         │   Persist  → Store (group commit)  │  │
 │                                                         └────────────────────────────────────┘  │
 └───────────────────────────────────────────────────────────────────────────────────────────────┘

Crates

CrateResponsibility
cronoq-coreThe scheduling engine. No clock, threads, I/O or unsafe.
cronoqTokio driver, public API, executors, scalers, Store trait, telemetry, blocking facade.
cronoq-store-postgresDurable Store with leader election and fencing.
cronoq-store-sqliteDurable Store in a local file, with a lease for leader election and fencing.
cronoq-protoProtobuf definitions (vendored protoc, no system dependency).
cronoq-servergRPC and HTTP services, remote executor, auth, metrics, worker client.
cronoq-pyPyO3 bindings (abi3), compat shim and gRPC client.
cronoq-cliThe cronoq command-line client for the server, with a live top view.
cronoq-operatorKubernetes operator that scales worker Deployments to their pool's desired capacity.
cronoq-store-testsConformance tests every durable store must pass.

A sans-IO engine

Core<P> is a deterministic state machine. Each call receives the current time; the engine updates its state and queues actions for the driver (dispatch this task, abort that lease, request N workers, persist these mutations, publish this status). It never reads a clock or performs I/O.

Consequences:

  • Determinism. The same inputs produce the same outputs. Randomness (retry jitter) comes from a seeded generator.
  • Testability. The test suite drives the engine under virtual time: hours of simulated workload run in milliseconds.
  • Portability. The same engine powers the Rust runtime, the Python bindings and the server; a different runtime (another async executor, a simulator) only needs a new driver.

Data structures

StructurePurposeCost
Slab of task entriesTask storage; queues hold 8-byte keysO(1) insert/remove
Per-class FIFO lane (sorted deque by sequence)Ready tasks without deadlines, in pools without fairnessO(1) push/pop; O(n) reinsertion for retries (binary search, then insert into the deque)
Per-class deadline queue (B-tree by latest start time)Ready tasks with deadlinesO(log n)
Per-class fair-share queue (B-tree by start tag)Ready tasks without deadlines, in pools with fairnessO(log n)
Due queue and pending holdsFixed-start tasks at or near their startO(log n)
Reservation calendar (B-tree by start)Admission of fixed startsO(k log k) for k overlapping
Resource waiter queuesTasks parked on a resource, FIFOO(1) amortized
Label waiter queues (one FIFO per label constraint)Tasks waiting for a free slot on a worker with the labels they requireO(log c) for c constraints, then O(1)
Dependency counters + reverse edgesDAG resolutionO(children) per completion
Timer heapRetries, holds, starts, misses, timeouts, leases, refills, autoscaling, cronO(log n), lazy invalidation
Retention tableTerminal statuses for late dependents and dedupO(1)
Window ring + log-linear histogramsMetricsO(1) per event

Dispatch compares the heads of at most eight queues (for each of the four classes: the deadline queue and either the FIFO lane or, in pools with fairness, the fair-share queue), computing their effective scores lazily. Every task in a class ages at the same rate, so in a FIFO lane the oldest task is always the best of its lane. The deadline queue's head is the task with the earliest latest start time, and the fair-share queue's head is the task with the lowest start tag. No periodic rescoring is needed.

Driver loop

The driver task waits on five sources (executor feedback, commands, handle cancellations, the store commit in flight and the engine's next timer), applies them to the engine, then executes the resulting actions. With a durable store, the mutations queued since the last commit are written in one transaction while the loop keeps running. Replies, dispatches and status updates that depend on those mutations are held until that commit succeeds, so acknowledgements imply durability.

Server

The server wraps a Scheduler<Bytes, Bytes> whose pools all use a remote executor: a dispatch becomes a Lease message to a connected worker with a free slot, and the attempt's future resolves when that worker reports back, disconnects, or the engine aborts the attempt. Pool capacity is the sum of connected workers' slots.

Testing strategy

LayerApproach
EngineUnit tests per feature; property tests for data structures; randomized deterministic simulation: hundreds of seeded workloads mixing microtasks, heavy-tailed jobs, deadlines, fixed starts, DAGs, resources, failures, cancellations and capacity changes, with internal invariants checked after every step and end-to-end properties (dependency order, FIFO within class, every task terminates) checked at the end.
RuntimeTests under tokio's paused clock; OpenTelemetry export test.
StoreOne conformance suite run against every store (throwaway PostgreSQL servers, temporary SQLite files); fencing, pg_terminate_backend failover, lease expiry and takeover.
ServerReal gRPC/HTTP listeners with real workers; active-passive failover test.
Pythonpytest suites for the bindings, the compat shim and the client against a real server, run against built wheels on Linux, macOS and Windows.
Gogo test -race for the SDK against a real server, with the generated stubs checked against the protocol.
TypeScriptnode:test suites for the SDK against a real server on Node 22 and 24, and the gRPC-Web client in headless Chromium; the generated code is checked against the protocol.
KubernetesThe Helm chart rendered with several value sets, each configuration checked by cronoq-server --check; on a kind cluster, failover between two replicas on PostgreSQL, a rolling upgrade, a two-shard release, and the operator scaling a worker Deployment.
PerformanceCriterion benchmarks with throughput gates in CI.