Expand description
Attempt trait-method family — Postgres implementation.
RFC-v0.7 Wave 4b. Bodies for claim, claim_from_reclaim,
renew, progress, complete, fail, delay, wait_children.
§Fence-triple invariants (RFC-003)
Every RMW against ff_attempt is wrapped in a BEGIN/COMMIT + a
SELECT ... FOR UPDATE of the target attempt row. The claim cascade
uses FOR UPDATE SKIP LOCKED on the exec_core + attempt join so two
workers racing on the same lane cannot double-claim. Lease epoch is
the authoritative fencing token: every terminal/progress/renew op
first re-reads the attempt row under lock and compares
lease_epoch against the handle’s embedded epoch. Mismatch →
EngineError::Contention(LeaseConflict).
§Isolation level (Q11)
Default READ COMMITTED. The claim query uses SKIP LOCKED to
keep the scanner contention-free; terminal ops use row-level
FOR UPDATE on the already-identified attempt row. This is enough
for the fence-triple invariant because the primary key on
ff_attempt is (partition_key, execution_id, attempt_index) —
the FOR UPDATE is partition-local and deterministic.
§Isolation note on capability matching
Capability subset-match runs in Rust after the FOR UPDATE SKIP
LOCKED row is obtained. If the worker does not satisfy the row’s
required_capabilities, we ROLLBACK (releasing the lock) and
return Ok(None) so another worker with the right caps can pick
the row up. We do NOT try to re-scan within the same claim call
— that would blow the blocking-wait budget in a pathological
mismatch loop; the caller’s retry cadence is the right scope.