ReactiveDag.Lock (reactive_dag v0.17.0-rc.65)

Copy Markdown View Source

A cluster-wide advisory lock, for the one thing that still needs one.

Propagation does not. A cascade takes no lock and a resumption takes no lock: at-least-once delivery over idempotent work is a stronger position than exactly-once scheduling, because it degrades gracefully rather than depending on job-state bookkeeping staying correct across a node death.

What still needs a lock is EXTERNAL I/O. Two nodes sweeping the same upstreams in the same minute is duplicated fetching — someone else's server, someone else's rate limit — and no amount of idempotence downstream makes those requests not happen. That concern is unrelated to propagation, which is why this survived the queue it used to live in.

A Postgres advisory lock because the requirement is exactly what they are for: cluster-wide, held on a connection, and released automatically if that connection dies — a node crashing mid-sweep does not leave the graph locked, which a lock table would.

Returns {:ok, result}, or :busy when another node holds it. Busy is not an error: the other node is doing this node's work.

case Lock.with_lock(fn -> sweep(plan) end, scope: tenant) do
  {:ok, result} -> result
  :busy -> :already_sweeping
end

Summary

Functions

with_lock(fun, opts \\ [])

@spec with_lock(
  (-> result),
  keyword()
) :: {:ok, result} | :busy
when result: term()