Skip to content

State stores

The state store is docsynth's single coordination point. It holds runs and their work units, hands out units atomically, tracks leases so a crashed worker's unit can be reclaimed, and carries the spend rollup and the provider quarantine set. Coordination lives here — not in a broker, not in the compute layer — which is what lets N workers drain one run with no leader (④ High throughput).

Chosen by URI (open_state): sqlite:// (default, single box) · firestore:// · dynamodb://.

The protocol (core/state/base.py)

StateStore is a Protocol; every backend implements the same surface.

Method Purpose
create_run(run, units) -> bool Conditional write — returns whether this caller planned the run (the rest wait).
get_run / set_run_state Read the Run; move it PENDING/RUNNING/PAUSED/CANCELLED/COMPLETED/FAILED.
claim_next_unit(run_id) -> WorkUnit \| None The atomic claim — hand exactly one pending unit to one worker, stamping a lease.
complete_unit / fail_unit Mark a claimed unit DONE, or FAILED with an error.
reset_failed_units / reclaim_expired_units Return failed units, or units with a lapsed lease, to the pool (resume / self-heal).
units / progress Iterate units; counts by WorkUnitState.
add_spend / spend / total_spend The per-(run, model) spend rollup, exact via integer nano-dollars.
quarantine_providers / quarantined_providers The set of providers a build has quarantined for empty output.
close Release resources.

Run and WorkUnit are the two records. A unit is an index range (start_index … end_index) — the single granularity of claiming, sharding, and export. A unit carries a lease_expires_at; lease_is_expired(now) is what makes reclaim possible.

The atomic claim

The claim is one conditional write — advance this unit to RUNNING only if it is still PENDING — implemented natively per backend:

  • SQLiteBEGIN IMMEDIATE; also reclaims expired leases opportunistically inside the claim's write lock, so a draining fleet self-heals without a resume.
  • Firestore — a transaction; reclaim is explicit (reclaim_expired_units), run on resume — a per-claim scan would be too costly at scale.
  • DynamoDB — a conditional UpdateItem (attribute condition state = pending).

A worker that loses the race simply advances to the next candidate. This is the same primitive used for run creation (ON CONFLICT DO NOTHING / document.create() / attribute_not_exists(pk)), so cold-starting N tasks produces exactly one planner and the losers wait.

Lease + reclaim

Each claim stamps a lease (default 15 min, configurable per store) and clears it on complete/fail/reset. reclaim_expired_units returns RUNNING units whose lease lapsed to the pool — so a worker that crashed mid-unit doesn't strand its work. Reclaim runs on resume_run and at the start of a catalogue build.

Work-unit lifecycle

stateDiagram-v2
    [*] --> PENDING: plan_units
    PENDING --> RUNNING: claim_next_unit (lease stamped)
    RUNNING --> DONE: complete_unit
    RUNNING --> FAILED: fail_unit
    RUNNING --> PENDING: lease expired → reclaim_expired_units
    FAILED --> PENDING: reset_failed_units (resume)
    DONE --> [*]

The run reaches COMPLETED only when no unit is PENDING/RUNNING/FAILED — and that transition is what writes the root manifest.

Spend & quarantine

add_spend increments a (run, model) row and a (run, "*") total in integer nano-dollars, so the counter is exact and a fleet-wide budget (DistributedBudgetGuard) can hold across N workers rather than per process. The quarantine set records providers a catalogue build found to return only empty completions, so later shards route around them.

Backends are verified against real emulators (SQLite in-process, the Firestore emulator, DynamoDB Local / moto). See Concurrency & coordination for the design.