# `DripDrop.Concurrency.AdapterLock`
[🔗](https://github.com/agoodway/dripdrop/blob/v0.1.0/lib/dripdrop/concurrency/adapter_lock.ex#L1)

Per-adapter Postgres transaction-scoped advisory lock for outbound dispatch.

Wraps the outbound deliver path so cap-gate evaluation, state transitions, the
actual provider send, and counter writes happen serially per adapter. Two
concurrent workers targeting the same adapter cannot both pass `MinGap`,
`RampCap`, and `SubCap` and burst past their thresholds; the loser receives
`:locked` and the dispatch path defers with `"adapter_busy"`.

Lifecycle dispatch is unaffected — only `deliver_outbound/2` calls this.

# `with_lock`

```elixir
@spec with_lock(Ecto.Schema.t(), (-&gt; term())) ::
  {:ok, term()} | :locked | {:error, term()}
```

Runs `fun` inside a transaction holding `pg_try_advisory_xact_lock` keyed by
the adapter's id. Returns `{:ok, result}` if the lock was acquired and `fun`
completed; `:locked` if another worker holds the lock right now; or
`{:error, reason}` for DB errors.

---

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