Skip to content

expose a version-fenced / atomic reclaim primitive on the sync path #26

Description

@fabracht

Summary

The MQTT sync path exposes no client-driven compare-and-set, so an atomic reclaim of an existing row (fence on an expected version/holder, then take it) cannot be expressed as a single operation. A caller must do delete(stale) → create(new), which is non-atomic and racy.

Why the create-conflict fix does not cover this

The Store::create rollback fix (stitch-sync 0.5.0) closes the fresh-claim-on-a-free-key race: two creates on a unique key → the loser now rejects with Conflict. But reclaiming a key already held by a departed/stale owner is a different problem: the client must atomically assert "the current holder is still X at version N" and replace it. There is no primitive for that.

Concrete downstream shape (ticket-live seat reclaim): acquireHold does store.delete(staleHold) then store.create(newHold) — two ops, no fence. Between them another client can claim the seat. This is the non-atomic-reclaim behavior Presence.tla breaks in a handful of steps.

What exists server-side vs. what the client can drive

  • mqdb has _version optimistic concurrency, and stitch already maps update/delete CAS conflicts to Error::Conflict.
  • BUT Store::update / Store::delete take no expected-version precondition from the client (update(entity, id, fields, origin) / delete(entity, id, origin)), and the agent's field-level LWW retry-on-conflict merges rather than rejects — the opposite of a fence. So the building block is present in the store but not surfaced as a client-controllable CAS.

Proposal (options to design between)

  1. Conditional write: an update/delete variant that takes an expected_version (or expected field values) and returns Error::Conflict when the precondition fails, disabling the LWW auto-merge for that call.
  2. Claim-transfer op: a dedicated "take ownership of id if holder == X and version == N" operation, returning Conflict otherwise — closer to a presence-lease.

Either lets a reclaim be a single fenced op instead of delete-then-create.

Notes

  • Overlaps the presence-lease / reclaim work in stitch-p2p (multi-leader LWW + HLC, signed reclaim), but that's the P2P crate — this request is for the MQTT/mqdb sync path (stitch-sync).
  • Architecture item, not a one-liner; worth designing deliberately (and probably a TLA+ model of the fenced reclaim before implementing).
  • Surfaced while integrating the seat-claim fix from roll back optimistic create on remote conflict (0.5.0 / 0.4.0) #22.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions