Docket.Backend behaviour (docket v0.1.0)

Copy Markdown View Source

Bundle contract for a durable Docket backend.

A backend is the configuration and substitution boundary. It supplies one transaction implementation and graph, run, and event capabilities that all understand the transaction context produced by that implementation. Store modules remain focused and independently testable, but callers must not assemble capabilities from unrelated backends.

The backend also owns its supervision entry point. child_spec/2 receives the options nested under {BackendModule, options}, the small set of runtime-owned policies needed for execution, and the runtime-generated name. The already-resolved opaque context remains a separate argument. The callback returns the single child specification the host places in its supervision tree.

Testing execution is also explicit. drain_runs/2 receives the same resolved context separately; :manual instances invoke it only through the public drain operation, while :inline instances invoke it after committed work is scheduled. Backends return a summary containing :limit_reached.

Summary

Types

A module implementing one of Docket's focused store contracts.

Opaque backend context passed through without interpretation by core.

Scope that determines graph/run ownership; tenant identifiers are non-empty.

Authorization and tenancy scope for a run or its events.

Callbacks

Builds the backend's supervision child specification from options and its resolved context.

Resolves the opaque root context passed to the backend transaction boundary.

Synchronously claims and drains due runs using the resolved backend context.

Returns the backend's Docket.Backend.EventStore implementation.

Returns the backend's Docket.Backend.GraphStore implementation.

Returns the backend's Docket.Backend.RunStore implementation.

Runs fun in one backend transaction.

Types

capability()

@type capability() :: module()

A module implementing one of Docket's focused store contracts.

ctx()

@type ctx() :: term()

Opaque backend context passed through without interpretation by core.

drain_summary()

@type drain_summary() :: %{:limit_reached => boolean(), optional(atom()) => term()}

owner_scope()

@type owner_scope() :: :tenantless | {:tenant, String.t()}

Scope that determines graph/run ownership; tenant identifiers are non-empty.

scope()

@type scope() :: :system | :tenantless | {:tenant, String.t()}

Authorization and tenancy scope for a run or its events.

transaction_fun()

@type transaction_fun() :: (ctx() -> transaction_result())

transaction_result()

@type transaction_result() :: {:ok, term()} | {:error, term()}

Callbacks

child_spec(opts, ctx)

@callback child_spec(opts :: keyword(), ctx()) :: Supervisor.child_spec()

Builds the backend's supervision child specification from options and its resolved context.

context(opts)

@callback context(opts :: keyword()) :: ctx()

Resolves the opaque root context passed to the backend transaction boundary.

drain_runs(ctx, opts)

@callback drain_runs(ctx(), opts :: keyword()) ::
  {:ok, drain_summary()} | {:error, term()}

Synchronously claims and drains due runs using the resolved backend context.

events()

@callback events() :: capability()

Returns the backend's Docket.Backend.EventStore implementation.

graphs()

@callback graphs() :: capability()

Returns the backend's Docket.Backend.GraphStore implementation.

runs()

@callback runs() :: capability()

Returns the backend's Docket.Backend.RunStore implementation.

transaction(ctx, transaction_fun)

@callback transaction(ctx(), transaction_fun()) :: transaction_result()

Runs fun in one backend transaction.

The callback receives a transaction-scoped opaque context, which must be passed to every graph, run, and event operation participating in the transaction. It returns {:ok, value} to commit or {:error, reason} to roll back. The backend returns that result unchanged, which lets lifecycle code compose store operations naturally with with.

Exceptions and throws also roll back, then propagate unchanged. A backend joins a transaction already represented by ctx rather than opening an invalid nested transaction. Returning any other shape raises ArgumentError and rolls back. If a nested callback fails and its result or raised value is swallowed, the containing transaction is rollback-only and returns {:error, :rollback} instead of publishing partial work.

Transaction-scoped describes participation, not value lifetime or identity. A backend may yield an ephemeral transaction object or reuse a normalized root-context representation whose active transaction is owned by the process, connection, or substrate. Callers must use the yielded value unchanged inside the callback and must not rely on its behavior afterward.

Publication must be concurrency safe. An implementation may serialize transactions or compare-and-swap their publication, but it must never take an unlocked snapshot and later replace newer committed state blindly.