Acta. apache-2.0 · .net 10 · pg / mssql / sqlite

Cluster-wide

Add as many workers as you like. They share the work through your database.

Every worker is a peer: the same process loop, talking to the same rows. There is no control plane to run, no broker to stand up, and nothing to elect. Add a process and it starts claiming. Kill one and the others carry on, and the work it held comes back through a recovery pass any of them can run.

registerheartbeatclaim due rowsleaserun handlersrenewsettlerepeatthe same ledger

One namespace, as the database sees it.

Three workers on build 1 share the namespace. Six jobs are due, J4 and J5 carry the concurrency key rebuild:acme, and J6 belongs to a definition the next build drops. Step through a claim, a heartbeat, a kill, the recovery pass, a shared gate, and a rolling deploy.

Worker processes · peers
W1build 1
Registered · beat 1 s ago
W2build 1
Registered · beat 1 s ago
W3build 1
Registered · beat 1 s ago
W4
Not started
Acta SQL ledger · PostgreSQL or SQL Server · namespace orders
Job rows in the namespace: status, the worker holding the lease, the lease window, the execution number, and a note
jobstatusleased byleaseexecutionnote
J1Ready··0
J2Ready··0
J3Ready··0
J4Ready··0key rebuild:acme
J5Ready··0key rebuild:acme
J6Ready··0handler in build 1 only
Namespace rowrecovery passDue in 40 s
Concurrency key rowrebuild:acmeFree
Rate meter rowsend-invoice · 10/sIdle

Workers find new rows by polling SQL, a 1 s safety poll by default. The optional Redis wake-up only shortens that wait; it never carries truth.

1 / 8 · PeersThree peer processes, one database, no leader.

Each worker registers in the namespace, heartbeats, claims due rows, runs handlers, and refreshes its leases. Nothing else coordinates them: no control plane, no broker, no election. The workers table is the only membership list, and every worker is in it as an equal.

Times are the defaults: 45 s heartbeat, 180 s lease, 315 s before a silent worker is marked dead. Execution stays at-least-once, so a handler with external effects is idempotent on every path. SQLite is the embedded single-node provider; the fleet story is PostgreSQL and SQL Server. Production guide · 705-worker-crash-recovery

The claim is the election

sys.retention, sys.alerts and sys.outbox are recurring slots like any schedule: each fires once per cadence, run by whichever worker claims it. Crash recovery rides the heartbeat instead, so it cannot get stuck behind the crash it repairs. No worker is special, so there is nothing to fail over.

One setting, three windows

The heartbeat interval is the only coordination knob. The lease window is four heartbeats and the dead-worker threshold seven, both read-only, so replicas that agree on the heartbeat agree on everything else by construction.

Measured, under chaos

The rc.5 certification ran 24 worker processes of 64 executors each, 1,536 concurrent slots on one database, with a worker killed every five seconds. Every one of a million jobs finished and no recorded step ran twice, checked afterwards from the database's own ledger, on PostgreSQL and on SQL Server. What one run showed, not a capacity claim. Certification records

Also: how one worker runs a job·which job runs next·order, concurrency, and rate