You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Store::create is not transactional when reusing an existing id #23
Store::create is not fully transactional when called with an id that already exists in the local store. On a remote rejection (409 Conflict / 403 Ownership), the rollback deletes the row at that id — losing the pre-existing row instead of restoring it.
Scenario
A legitimate local row X exists at id = K (e.g. holding unique key S1).
A caller invokes create(id = K, …) with a different non-id unique field value (e.g. S2) that is already held remotely by another row.
create optimistically upserts memory/persistence at K (overwriting X), then sync_create returns 409.
rollback_local_create deletes id = K → both the new data and the original X are gone.
This only bites when create() is used with an already-existing id to change a non-id unique field — effectively using create() where update() is meant. Claims that use a fresh id (the intended exclusive-key pattern) are already transactional.
Verification
Modeled in TLA+ (CreateRollback): the current delete-by-id rollback satisfies a transactional invariant (phase=done ⇒ local = prior) when prior = none (fresh id) and violates it when prior = legit (id reuse). A snapshot-restore variant satisfies it in both cases.
Notes / options
The loss originates in create()'s pre-existing upsert-over-existing semantics (step 3), not the rollback itself.
A partial snapshot-restore only covers the synchronous online-create path; the transient-then-flush rollback (flush_loop / on_connected) cannot restore prior state because it isn't captured there.
Cleanest fix: make create reject an Origin::Local insert when the id already exists locally (create ≠ upsert), which is correct-by-construction and cheap (one local read for Origin::Local). This is a behavior change (create-over-existing-id currently upserts), so it warrants its own PR.
Follow-up from the review of #22. Documented as a known limitation on Store::create in that PR.
Activity