How BackWave Works

A two-minute mental model of a job’s life and the Core/Shell split that makes BackWave deterministic and testable.


The life of a job#

Every job follows the same path: enqueue → durable row → claimed under a Lease → runs once with retries → terminal.

  • Enqueue. You enqueue a typed job with a due time. "Enqueue now" is just a due time of now, so there is no separate immediate path. Every job is a Scheduled Job, and a due time is the only shape of work the Core understands.
  • Durable in your database. The job becomes a row in the database you already run, and that row is the single source of truth. When BackWave's tables share that database, you can enqueue inside your own transaction and the job commits or rolls back atomically with your business write, with no outbox and no dual-write race.
  • Claimed by a Worker. Workers poll the database and claim due jobs, taking a Lease on each. A Lease is a time-bounded, heartbeat-renewed claim, not a lock, and claiming one starts an Attempt.
  • Runs once, with retries. The handler runs for one Attempt. Return is success; throw is failure. A failure with retries left reschedules; a failure that exhausts the retry budget is Dead-Lettered.
  • Terminal. The job settles in a terminal state and stops moving on its own. Only an explicit Operator Action, such as a requeue, moves it again.

Delivery is at-least-once. A Lease can expire on a crash or a network cut, so a job can run again, which means handlers must be idempotent. The full state machine and retry loop live in Job Lifecycle, and how at-least-once pairs with the Effect-Once fence to keep repeated runs safe lives in Execution Guarantee.

The Core decides, the Shell acts#

BackWave splits its runtime into two halves. The Core is pure decision logic: given a snapshot of state, an event, and the current time, it returns the decisions that follow, such as when a job is due, whether a failure retries or dead-letters, and when a Lease has expired. It does no I/O and never reads the wall clock. The Shell is the thin imperative loop around it. It polls the database, hands the current state to the Core, and carries out the resulting instructions, owning all the concurrency, the sockets, and the real clock. The database row is the single source of truth, and every node is an interchangeable stateless peer, so you scale by adding processes.

That split is what makes your job logic testable. Because the Core does no I/O and never reads the wall clock, the same inputs always produce the same decisions. You can drive scheduling and retry logic from a test on a clock you control, and years of schedule activity run in milliseconds. That same determinism is why BackWave can be simulation-tested for correctness against seeded fault injection. See Execution Model for the split and stateless-peer scaling, and Virtual-Time Testing to drive the runtime deterministically yourself.

Where to go next#

  • Job Lifecycle: the seven states, the retry loop, leases, and terminal transitions.
  • Execution Model: the Core/Shell split, the database as single source of truth, and stateless-peer scaling.
  • Execution Guarantee: at-least-once execution, the Effect-Once fence, and how to write idempotent handlers.
  • Quickstart: wire BackWave up and run a real job.