Glossary

The canonical definition of every BackWave term, from job states and queues to leases, dependencies, and the storage contract.


This page is the single canonical home for BackWave's vocabulary. Every term below is defined once here, and the rest of the documentation links back to it. Definitions are grouped by area. Where a term has a full concept or reference page of its own, the definition links to it.

BackWave is a background job system: you enqueue work, it runs once on some worker, and it retries on failure. Job internals are opaque; BackWave never records or replays the steps inside a job body. It is not a durable execution or workflow engine in that sense.

Architecture and determinism#

BackWave separates the decisions about what should happen from the machinery that makes it happen. The decision logic is a pure, deterministic layer; everything with I/O, concurrency, and a real clock sits around it. The determinism boundary is the line between the two, and it is the same line the test simulator can reproduce exactly.

TermDefinition
CoreThe pure decision logic: scheduling, retries, due calculation, lease and timeout handling, and state transitions. Deterministic functions of state, event, and time. The Core performs no I/O and never reads the wall clock.
ShellThe per-node imperative loop and its edges: fetch state, call the Core, and execute the resulting Commands through the storage contract. The Shell owns all concurrency, I/O, and the real clock.
CommandA value the Core returns describing what the Shell should do, such as mint a job, schedule a retry, expire a lease, or claim from a queue. The Core decides; only the Shell acts.
EventAn input to the Core, the counterpart to a Command.
Node DriverThe sans-I/O state machine holding all of a node's logic (claiming, heartbeats, lease renewal, hint reactions) as a single Step(event) that returns Commands. It never awaits, times, or threads; the Shell's Pump and the Simulator are its two callers. See The Sans-IO Node Driver.
Determinism boundaryThe line between what is simulable and what is not. Everything inside is deterministic and reproducible; everything beyond it is not. Realized in code as the storage contract. See The Determinism Boundary.
Virtual TimeA controllable clock that tests advance explicitly. The Core never reads the wall clock; time is supplied through a TimeProvider whose default is TimeProvider.System, and a test harness supplies a virtual clock instead.

Storage contract and adapters#

Every place BackWave persists state goes through one seam with a precise behavioral specification. Production implementations target real databases; a first-class in-memory implementation ships for tests and local development.

TermDefinition
Storage contractThe semantic specification every storage implementation must guarantee, including behavior under concurrent lease acquisition and crash mid-write. Realized as the IJobStore interface. Implementations must not read their own clock; time is always passed as an explicit now parameter. See The Storage Contract.
Storage adapterA production implementation of the storage contract against a real database. Version 1 ships adapters for Postgres, SQL Server, and SQLite. Verified by the conformance suite.
Networked adapterA storage adapter over a database server reachable across hosts: Postgres and SQL Server.
Embedded adapterA storage adapter whose database is in-process or single-host. Version 1's embedded adapter is SQLite. Durable and conformance-verified, but bounded to one host. See SQLite.
Co-resident deploymentAn embedded-adapter deployment where BackWave tables live in the application's own database file, giving the tightest transactional enqueue.
Dedicated deploymentAn embedded-adapter deployment where BackWave uses its own database file, which forgoes transactional enqueue.
In-memory storeA first-class, publicly shipped IJobStore implementation that is deterministic and runs on Virtual Time. It persists nothing and is single-process, so it cannot carry the execution guarantee; its home is tests and local development.
Transactional enqueueEnqueueing a job inside the application's own database transaction so the business write and the job commit or roll back atomically. A storage-contract capability, not universal; surfaced as IJobStore.SupportsTransactionalEnqueue. See Transactional Enqueue with EF Core.
Schema nameThe database schema (Postgres, SQL Server) or table-name prefix (SQLite) holding all of an adapter's objects. Default "backwave", configurable per store and fixed for the life of the data.
In-place upgradeA schema upgrade applied to a live database with no drain or maintenance window. Supported for every adapter. See Schema Migrations.
Mixed-version fleetRunning more than one BackWave version against the same database at once. Supported at N-1 version skew only.

Jobs, handlers, and identity#

A job is a unit of work with a stable wire identity and a handler that runs it. Identity in storage is always explicit and never derived from a class name, so renaming code never changes what is on the wire.

TermDefinition
Scheduled jobA job with a due time, the only shape of work the Core knows. An "enqueued" job is simply one whose due time is now, on the same code path.
Wire NameA job type's mandatory, explicitly declared identity in storage. Never derived from CLR type names, so renaming a class never changes it. Declared with [Job("wire-name")]. See Job Attributes.
[Job] attributeThe attribute that declares a job type's Wire Name and defaults. Applies to a payload record (class form) or a handler method (method form). See Jobs and Handlers.
HandlerAn implementation of IJobHandler<TJob> with a single HandleAsync method. Returning normally means success; throwing means failure and schedules a retry until the attempt ceiling. Every handler may run more than once, so idempotency is the author's responsibility.
AttemptOne execution try of a job, numbered and visible to the handler through JobContext.Attempt. The first attempt is 1. A lease expiry counts as an attempt, the same as a thrown exception.
JobContextThe execution context for one attempt. Carries JobId and Attempt, buffers tag additions and job output, and can pull a dependency ancestor's output. See Jobs and Handlers.
Job ManifestA committed snapshot of every registered Wire Name, verified by a shipped test helper so wire-format changes appear in pull-request diffs. See Guard Wire Compatibility.

The [Job] attribute carries these properties.

PropertyTypeDefaultMeaning
WireNamestringrequiredThe job type's storage identity, unique across all jobs.
Queuestring"default"The Queue jobs of this type go to unless overridden at enqueue.
Labelsstring[][]Default tag Labels applied to every job of this type. Additive only.
OrderCharged.cs
[Job("order-charged", Queue = "billing")]
public sealed record OrderCharged(Guid OrderId);

Job states#

A job moves through a small fixed set of states. Four are terminal; the rest are live. The enum values, their canonical dashboard spellings, and their meanings are below. The full lifecycle and the legal transitions live on Job Lifecycle and Job States.

StateDashboard spellingTerminalMeaning
ScheduledScheduledNoEnqueued and waiting for its due time; eligible to be claimed once due.
AwaitingParentAwaiting ParentNoHeld back until its parents reach a terminal state; becomes Scheduled once the last parent resolves.
LeasedLeasedNoClaimed by a worker and running under a lease that must be renewed by heartbeat.
SucceededSucceededYesCompleted successfully.
CancelledCancelledYesCancelled by an operator, or because an on-success parent failed.
DeadLetteredDead-LetteredYesExhausted its retry budget and set aside for inspection.
QuarantinedQuarantinedYesCould not be routed to a handler and was set aside.

JobStates.IsTerminal returns true for Succeeded, Cancelled, DeadLettered, and Quarantined, and false otherwise.

TermDefinition
Terminal stateA state a job never leaves under any automatic path: Succeeded, Cancelled, Dead-Lettered, or Quarantined.
Dead-LetteredTerminal state of a job that ran and kept failing until it exhausted its attempt ceiling.
QuarantinedTerminal state of a job that could not be routed or decoded: its Wire Name has no registered handler, or its payload no longer deserializes. Loud and visible, never a silent retry loop.
Terminal CauseA short human-readable reason for the terminal state (the failure error, cancel actor, or unroutable reason), or null while the job is live. Distinct from Failure Detail, which is the diagnostics of one failed attempt.

Enqueueing#

The client is the single entry point for putting work into BackWave. Every enqueue produces a scheduled job with a due time; enqueueing "for now" just sets the due time to now.

TermDefinition
EnqueueAsyncEnqueues a job with a due time and an optional queue, tags, and transaction. Returns the new job's Guid.
EnqueueDependencyAsyncEnqueues a job that waits until a parent job, identified by its Guid, reaches a terminal state before becoming due. See Dependencies.
UpsertRecurringAsyncDefines or redefines a recurring schedule from a cron expression and a template. Only future ticks are minted; defining does not back-fill.
RemoveRecurringAsyncRemoves a recurring schedule. Already-minted instances run to completion; removing an unknown schedule is a no-op.
Enqueue resultThe outcome of an enqueue attempt: Ok, Duplicate, PayloadTooLarge, WireNameTooLong, UnknownParent, or TooManyParents.

Type-default tags declared on the [Job] attribute are additive only; they are always unioned into the caller's tags, never subtracted. The full signatures and error behavior live on the Client API reference.

Scheduling#

A recurring schedule is a cron-defined template that mints scheduled jobs as time passes. The schedule and the jobs it mints are distinct things with distinct lifecycles.

TermDefinition
Recurring ScheduleA cron-defined template that mints scheduled-job instances over time, in UTC by default or in an opt-in IANA time zone. See Scheduling.
CronA standard cron expression, either 5-field (minute, hour, day-of-month, month, day-of-week) or 6-field with a leading seconds field. No Quartz extensions; any other field count is rejected. Schedules store the canonical 6-field, seconds-first form. See Cron API.
Catch-Up PolicyWhat a schedule does about ticks missed while nothing was minting them: Skip (the default; mint nothing) or Coalesce (mint exactly one make-up run for the whole missed set). Replaying every missed occurrence is deliberately unsupported.
No-OverlapA schedule setting that suppresses minting a new instance while a previous one is still non-terminal; the skipped tick is recorded visibly. Behaviorally equivalent to a per-schedule concurrency limit of 1, enforced at mint time.
Time zoneThe IANA time zone a schedule's cron is interpreted in, for example America/New_York. Null means UTC. An unresolvable zone is rejected.

The cron field ranges are seconds 0-59, minutes 0-59, hours 0-23, day-of-month 1-31, month 1-12, and day-of-week 0-7. When both day fields are restricted they combine with OR; otherwise they combine with AND. The Cron fluent builder (EveryMinute, Hourly, Daily, Weekly, Monthly, and others) produces these expressions; see Cron API.

Queues, workers, and dispatch#

A queue is a named stream of jobs claimed in due-time order. Consumers are organized into worker groups, each of which declares the queues it serves and how it shares effort across them. Priority lives on the consumer side, never on the job.

TermDefinition
QueueA named stream of jobs, claimed in due-time order. A job belongs to exactly one Queue, declared on its type and overridable at enqueue. See Queues.
Paused QueueA Queue that yields nothing on claim until it is resumed. Paused and resumed by an operator action.
WorkerOne execution slot in a worker group's pool. Pool size is the group's PoolSize. Execution concurrency, distinct from a Pump's fetch-loop parallelism.
PumpThe Shell-side event loop running one worker group's claim, dispatch, and report cycle, feeding events to a single driver. A group's store I/O is serial within one Pump. A group runs one or more Pumps. See The Job Pump.
Worker GroupOne registered set of workers in a process, declaring which queues it serves and its dispatch policy. See Worker Groups and Dispatch.
Dispatch PolicyHow a worker group shares claim effort across the queues it serves: Strict (fixed priority order) or Weighted (smooth weighted round-robin). Both are work-conserving. See Dispatch Policies.
Concurrency LimitA per-Queue, cluster-wide cap on simultaneously executing jobs, enforced at claim time. One shared counter across the cluster per Queue. A slot is released on terminal state or lease expiry, never leaked by a crash.
BackpressureNode-local flow control: a node stops claiming when its worker pool has no free worker, so claims never exceed free capacity. Independent of the cluster-wide Concurrency Limit.
Wake-Up HintAn optional storage notification ("something was enqueued, poll now") that exists only to cut claim latency. Never correctness-bearing: the system behaves identically, minus latency, if every hint is dropped, duplicated, or delayed. Polling is the sole source of truth. See Wake-Up Hints.

A worker group is configured with these options and defaults.

PropertyTypeDefaultMeaning
NamestringrequiredUnique within one registration; appears in health, metrics, and logs.
PolicyDispatchPolicyrequiredWhich queues the group serves and how effort is shared.
PoolSizeint20Max concurrent jobs per node; polling pauses while full.
Pumpsint1Independent pump loops in one process. Must be at least 1.
MaxClaimBatchint32Max jobs claimed per poll.
MaxOutcomeBatchint?MaxClaimBatchMax completed outcomes buffered before one batched write.
PollIntervalTimeSpan1 secondHow often the group polls for new work.
MaintenanceIntervalTimeSpan5 secondsCadence of lease expiry, schedule minting, and retention purge.
LeaseDurationTimeSpan60 secondsHow long a claimed job's lease is held before it lapses.
HeartbeatIntervalTimeSpan?one third of LeaseDurationLease renewal cadence.
RetryPolicyRetryPolicyRetryPolicy.DefaultBackoff and attempt ceiling.
RetentionRetentionPolicy?RetentionPolicy.DefaultTerminal-job keep-then-purge; null disables retention sweeping.

Leases and delivery guarantees#

A worker holds a job under a time-bounded lease it renews by heartbeat. If the lease lapses, the job becomes claimable again. This is the mechanism behind at-least-once delivery, and it is why handlers must be idempotent.

TermDefinition
LeaseA worker's time-bounded, heartbeat-renewed claim on a job. Expiry makes the job claimable again. A lease expires on its own; it is not a lock. See Leases and Crash Recovery.
At-Least-Once ExecutionBackWave's delivery contract: a handler body may run more than once, and idempotency is the author's responsibility. Exactly-once body execution is not offered. See The Execution Guarantee.
Effect-OnceThe property that despite at-least-once execution, the recorded outcome of an attempt and every state transition flowing from it (the terminal state, the dependency latch decrement, the concurrency-limit slot release) apply exactly once, caused by the node holding the live lease for that exact attempt. See The Effect-Once Fence.
Stale-lease beliefThe condition of an isolated node that keeps executing while believing it still holds a lease it has actually lost. Its late outcome is fenced out and changes nothing.

Retries and retention#

A job that fails is retried on a backoff until it reaches its attempt ceiling, at which point it is dead-lettered. Terminal jobs are kept for a policy-defined window and then purged.

TermDefinition
RetryAn attempt after the first. Governed by a RetryPolicy with a backoff function and an attempt ceiling. See Configure Retries and Error Handling.
Retry policyMaxAttempts (default 10) plus a Backoff function. The default backoff is 2 raised to the attempt number in seconds, capped at 5 minutes (300 seconds). When a failed attempt reaches MaxAttempts, there is no next attempt and the job is dead-lettered.
Retention policyKeep-then-purge for terminal jobs, timed from the instant a job reached a terminal state. Succeeded and Cancelled are kept 24 hours; Dead-Lettered and Quarantined are kept 14 days. See Retention and Purge.

Limits and bounds#

Storage enforces a set of named limits. Most are enforced by rejecting or clamping the write; two payload-shaped limits differ deliberately. The full table with rationale lives on Limits and Defaults.

LimitDefaultBehavior on exceed
MaxPayloadBytes65,536Reject enqueue
MaxWireNameLength128 charactersReject enqueue
MaxClaimBatch32Clamp down
MaxParentsPerJob16Reject enqueue
MaxMonitorPageSize200Clamp down
MaxPurgeBatch500Clamp down
MaxRecordedSkippedTicks32Age out oldest
MaxTransitionsPerJob64Drop oldest transition
MaxFailureDetailBytes8,192Truncate (never rejects)
MaxOutputBytes65,536Reject (never truncates)

Failure Detail is write-only diagnostics, so an over-limit value is truncated. Job Output is functional data a descendant will deserialize, so an over-limit value is rejected rather than silently corrupted.

Tags#

A tag is an observational annotation for search, filter, and grouping in the Monitor and Dashboard. Tags are purely descriptive: the Core never reads a tag, so tags never cross the determinism boundary. A tag is one of two structurally distinguished kinds.

TermDefinition
TagAn observational annotation on a job for search, filter, and grouping. Never read by the Core. See Tags.
LabelA bare-string tag, for example urgent. A colon inside a Label is ordinary data, never a separator.
Keyed TagA key-and-value tag, for example tenant to acme. One key may carry several values.
JobTagsA set of tags: re-adding an identical tag is a no-op, iteration is in first-seen order, and equality is order-independent. Set semantics keep tag authorship idempotent under at-least-once execution.

Tag values are strings only; a date is a caller-canonicalized string, with no numeric or date range semantics. In the Dashboard a Label renders as its bare value and a Keyed Tag renders as key:value for display only; the colon is never parsed.

Dependencies and workflows#

A dependency is a static edge from a job to a set of parent jobs whose terminal states gate the child's due-ness. Workflows are a Pro-tier grouping and identity layered over jobs connected by dependency edges.

TermDefinition
DependencyA static edge from a job to a parent set whose terminal states gate the job's due-ness, a countdown latch. The orchestration mechanism, below the determinism boundary. Edges are static, declared at the dependent's enqueue time. See Dependencies.
Dependency modeHow a dependent reacts to its parents: OnSuccess (the default; release only if every parent Succeeded, and cancel the dependent on any other terminal outcome) or OnAnyTerminal (release once every parent is terminal, whatever the states).
Awaiting ParentThe state of a job whose dependency parent set is not yet fully terminal.
Job OutputThe opaque blob a handler optionally emits on success, the success-side twin of Failure Detail. Written to the job row atomically with the Succeeded transition under the same Effect-Once fence, independent of the job history policy, and read lazily by a descendant. Bounded by MaxOutputBytes; over-limit is rejected, not truncated. See Read Another Job's Output.
Dependency outputThe result a descendant gets when it pulls an ancestor's Job Output: the ancestor's state, whether output is present, and the value. Only transitive ancestors are readable, never a non-ancestor sibling. Absence is a normal result, not an error.
WorkflowThe user-facing grouping and identity over jobs connected by dependency edges: a name, a sortable ID, a graph view, and lifecycle operations. Lives entirely above the determinism boundary; its status is always a projection of member-job states, never stored. A job belongs to at most one Workflow. A Pro feature. See Workflows.
Workflow statusA projection over member-job states with first-match-wins precedence Running, Failed, Cancelled, Succeeded: Running if any member is non-terminal, else Failed if any member is Dead-Lettered or Quarantined, else Cancelled if any is Cancelled, else Succeeded. Failure dominates.
Workflow RestartRecovery by re-instantiating a Workflow's definition as a brand-new Workflow with fresh job identities, optionally linked by lineage. Always re-runs the whole graph from the start, a redo rather than a resume. Ships in BackWave Pro.

History, observers, and monitoring#

BackWave keeps a per-job history of state changes and exposes two ways to consume the lifecycle: a pull-side Monitor and a push-side Observer. What history is recorded is governed by a policy.

TermDefinition
Transition LogAn append-only, per-job history of state changes the Monitor surfaces as a timeline. Each entry is a timestamp, resulting state, attempt number, and optional Failure Detail. Bounded by MaxTransitionsPerJob (64) and deleted with the job under retention.
Job History PolicyA ladder governing what the Transition Log records: Off (nothing), Transitions (transition rows, no failure detail), or TransitionsAndFailureDetail (the full log, and the default). The policy gates writes, never schema.
Failure DetailThe opaque diagnostic text (exception type, message, stack) captured at the edge when an attempt throws, attached to the failing transition entry. Never read by the Core. Bounded by MaxFailureDetailBytes (8,192) and truncated, never rejected. Viewing is gated behind the sensitive-data permission.
Transition ObserverHost-supplied, egress-only code BackWave invokes when a job reaches a declared state, the sanctioned way to react to the lifecycle. Observes transitions, never events, and can never alter a Core decision. Delivered at-least-once and not Effect-Once, so idempotency is the subscriber's responsibility. Requires a history policy of at least Transitions. See Observers API.
Observer subscriptionThe filter that selects which transitions an observer receives: a set of states, and optional Wire Name and Queue filters.
MonitorThe pull-side read surface, the Observer's twin. Reads jobs, job history, payloads, output, queue depths, tag facets, schedules, and observer cursors and lag. See Monitor API.

Operations and the dashboard#

Operators act on jobs and queues through a fixed set of state-machine transitions, each with recorded identity, never a raw row edit. The Dashboard authorizes these actions by asking the host application.

TermDefinition
Operator ActionA Dashboard- or API-initiated Core state transition with recorded identity: cancel a job, requeue a Dead-Lettered or Quarantined job to Scheduled, pause or resume a queue, set a concurrency limit, or trigger a schedule now. Editing a job's payload is deliberately not one. See The Dashboard.
Dashboard PermissionA capability the Dashboard checks before allowing an action: View, ViewSensitiveData, Requeue, Cancel, TriggerSchedule, and PauseQueue. Each is a delegated callback the host answers; BackWave never owns users or roles. View defaults to allow; every other permission defaults to deny.
Sensitive data exposureA host-level master switch plus an environment kill-switch that together decide whether raw payload bytes, Failure Detail, and Job Output can be viewed in the Dashboard. See Handle Sensitive Data.
Dashboard ExtensionA surface a separately installed Pro Dashboard package contributes to the free Dashboard: nav entries, a banner, page routes, and action routes. Each action route is gated by an existing permission. The Workflow surface is the first such extension.

Testing and simulation#

BackWave's correctness is verified by deterministic simulation and by suites that hammer real databases. These are internal testing infrastructure rather than public library API, but they are part of the project's vocabulary and explain how its guarantees are established. The Testing section covers what this means for your own tests.

TermDefinition
SimulatorA test harness driving many virtual node drivers and the in-memory store through compressed Virtual Time with seeded fault injection. One 64-bit seed fully determines a run.
SeedThe compact 64-bit discovery unit that fully determines a simulator run.
PlanA serializable, replayable description of one simulator run: a scenario plus a fault map addressing every injected fault by stable identity. What is minimized, replayed, and checked in as a regression.
SwarmPer-run randomization of the fault parameters themselves, a pure function of the seed. Not fuzzing: it randomizes fault configuration, not workload payload.
Fault LevelA contract chosen before a run, pairing the fault envelope the swarm may draw from with the oracles enforced: Pristine (no faults; full safety and strict liveness), Recoverable (self-healing faults; full safety and liveness once faults cease), or Adversarial (faults that may never heal; safety only).
Node IsolationA simulator fault cutting one node off from the storage contract for a bounded window; the node keeps executing under a stale-lease belief. Distinct from a crash, and never split-brain, because peers are database-authoritative.
Conformance SuiteA test suite verifying that a storage adapter honors the storage contract against the real database. See The Conformance Suite.
Torture SuiteA non-deterministic correctness instrument for adapters: a randomized concurrent workload hammers a real database, then invariants are audited over the final state and logs. A torture failure is always a bug.
Benchmark HarnessA macro end-to-end throughput tool driving the real Shell against a real adapter under wall-clock time. Deliberately outside the determinism boundary; it measures performance, never correctness. See Performance and Benchmarks.

BackWave Pro and licensing#

BackWave Pro is a commercial add-on feature set shipped as separate, publicly available packages layered on the free base. The free base is complete and production-grade, and free for everyone forever.

TermDefinition
BackWave ProThe commercial add-on feature set. Free to use for organizations under $1M annual revenue on the honor system; a paid, revenue-banded license is required above that. Features are identical regardless of tier; a license grants the permission to use Pro at commercial scale.
Soft enforcementHow a license is checked: an unlicensed production process runs normally but emits a startup log warning and a Dashboard banner. There is no hard failure, and the software cannot detect revenue.
License stateThe result of checking a license string: Valid (present, verified, in term; no warning), Missing (no license, the expected free-use state), Malformed (present but not well-formed or failed verification), or OutOfTerm (genuine but the subscription term ended). Pro never changes behavior based on this value.
Revenue bandThe self-reported revenue tier in a license. It sets price only, never which features run.

Where to go next#

Found a problem on this page? Report an issue