Skip to content

Storage ​

WorkflowStorage is the durable contract. The in-memory adapter is for tests. SupabaseWorkflowStorage is the PostgreSQL adapter.

The durable adapter persists the runtime kernel in a dedicated product-owned schema (normally workflow_runtime):

  • workflow_versions
  • workflow_runs
  • workflow_events
  • workflow_commands
  • a queryable work-item projection
  • an idempotent signal inbox
dart
final storage = SupabaseWorkflowStorage(
  client: serviceRoleClient,
  durableStorage: SupabaseDurableStorage(client: serviceRoleClient),
  schema: WorkflowRuntimeSchema.defaultWorkflowSchema,
);

The product repository owns the generated baseline and every forward migration. Apply database migrations before starting a server built against a new storage contract. At startup, call verifyDecisionFencingCompatibility() before workers begin polling; an unmigrated or incorrectly granted database must fail closed.

Decision concurrency contract ​

A decision is one closed durable operation:

  1. Atomically claim an exact run with a worker ID, claim generation, and lease.
  2. Load and deterministically evaluate the claimed revision.
  3. Commit the run, history events, commands, continuation, consumed signals, and output/error in one database transaction.
  4. Accept the commit only while the worker ID, claim generation, lease, and expected revision still match.

claim_lost, terminal, and revision mismatch are protocol outcomes, not generic database failures. They must not enter an unbounded retry loop. The unfenced commit function is internal and service_role execution is revoked. This permits many decider replicas to poll concurrently while allowing only the current claim holder to publish effects for a run.

The schema declaration rejects incompatible shapes. Products share the kernel through tenant_id and application_id.

The generated migration grants service_role only and enables RLS. Never place a service-role key in a Flutter client. The command table is the durable queue. An external broker, if introduced, must be fed through a transactional outbox.

Validate the real PostgreSQL contract with concurrent workers, expired-lease reassignment, stale-worker completion, duplicate delivery, and post-workload invariant checks. Compute-only tests do not prove locking, grants, or RPC atomicity.

See Also ​