Behaviour for lease-based distributed work claiming.
When configured, ensures at-most-once execution of episodes across
clustered nodes. Each episode's dedupe_key is claimed before execution —
only the node holding an active lease runs the work.
Configuration
# Use the built-in Ecto-based implementation:
config :cyclium, work_claims: Cyclium.WorkClaims.EctoClaims
# Or a SQL Server-optimized adapter in consuming apps:
config :cyclium, work_claims: MyApp.WorkClaims.SqlServer
# Or omit entirely — no claiming, fully backwards compatibleGate functions
All integration points use gate_* functions which return
{:ok, :passthrough} when no implementation is configured. This
makes work claims opt-in with zero impact on existing deployments.
Summary
Callbacks
Returns true if owner_node still holds the claim on dedupe_key at the
given fence (ownership generation). Optional — adapters that don't implement
it fall back to an owner-only ownership check via renew/3.
Functions
Attempt to acquire a lease on dedupe_key. Returns {:ok, :passthrough}
if work claims are not configured.
Mark work as completed. No-op if unconfigured.
Mark work as failed. No-op if unconfigured.
Returns true if owner_node still holds the claim at fence.
Lists up to limit claims whose lease has elapsed (still :claimed, past
lease_until) — read-only, does not transition them. Returns {:ok, []} when
unconfigured. See Cyclium.WorkClaims.EctoClaims.reclaim_expired/1.
Renew the lease for dedupe_key. No-op if unconfigured.
Types
@type acquire_result() :: {:ok, Cyclium.Schemas.WorkClaim.t()} | {:error, :busy}
@type owner_result() :: :ok | {:error, :not_owner}
Callbacks
@callback acquire(dedupe_key :: String.t(), owner_node :: String.t(), opts :: keyword()) :: acquire_result()
@callback complete(dedupe_key :: String.t(), owner_node :: String.t()) :: owner_result()
@callback fail(dedupe_key :: String.t(), owner_node :: String.t(), error_detail :: map()) :: owner_result()
@callback owns?(dedupe_key :: String.t(), owner_node :: String.t(), fence :: integer()) :: boolean()
Returns true if owner_node still holds the claim on dedupe_key at the
given fence (ownership generation). Optional — adapters that don't implement
it fall back to an owner-only ownership check via renew/3.
@callback reclaim_expired(limit :: pos_integer()) :: {:ok, [Cyclium.Schemas.WorkClaim.t()]}
@callback renew( dedupe_key :: String.t(), owner_node :: String.t(), lease_seconds :: pos_integer() ) :: owner_result()
Functions
Attempt to acquire a lease on dedupe_key. Returns {:ok, :passthrough}
if work claims are not configured.
Mark work as completed. No-op if unconfigured.
Mark work as failed. No-op if unconfigured.
Returns true if owner_node still holds the claim at fence.
Returns true (we own) when claims are unconfigured. When the configured
adapter implements owns?/3 and a fence is known, delegates to it
(fence-aware). Otherwise falls back to an owner-only check via renew/3,
preserving protection for adapters that don't implement the fence callback.
Lists up to limit claims whose lease has elapsed (still :claimed, past
lease_until) — read-only, does not transition them. Returns {:ok, []} when
unconfigured. See Cyclium.WorkClaims.EctoClaims.reclaim_expired/1.
Renew the lease for dedupe_key. No-op if unconfigured.