# `Cyclium.WorkClaims.EctoClaims`
[🔗](https://github.com/Cyclium/cyclium_ex/blob/main/lib/cyclium/work_claims/ecto_claims.ex#L1)

Default Ecto-based work claims implementation. Works with any Ecto adapter.

Acquisition is race-free without row locks: every takeover is a single
conditional `UPDATE ... WHERE <the state we read still holds>` and we treat a
zero-row result as "someone else won" (`:busy`). A brand-new claim is an
`INSERT` whose unique `dedupe_key` index turns a concurrent first-acquire into
`:busy` rather than a duplicate. This preserves at-most-once across nodes on
READ COMMITTED (the Postgres/SQL Server default) — two nodes can never both
believe they hold the same lease.

A SQL Server adapter using `UPDLOCK`/`READPAST` hints is an optional
throughput optimization, not a correctness requirement.

The `state` column is an `Ecto.Enum` — all reads and writes use atoms
(`:claimed`, `:done`, `:failed`). An expired lease has no distinct state: it's
a `:claimed` row whose `lease_until` has elapsed, which the takeover path
steals in place.

# `reclaim_expired`

Lists up to `limit` claims whose lease has elapsed (still `:claimed`, just
past `lease_until`). Read-only — it does **not** transition or re-acquire them;
a caller re-acquires each via `acquire/2`, whose takeover path steals the
expired `:claimed` row. Recovery uses optimistic `acquire` directly, so the
framework itself doesn't call this — it's here for external maintenance sweeps.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
