Relationships
#3377 S3/GCS datastores: write data version files create-only so a second writer cannot overwrite an existing version
Opened by stack72 · 10/13/2026
Background
swamp-club#3373: on S3 and GCS v2 datastores, a run whose model lock was force-released (swamp datastore lock release --force) still pushed. Each clone allocates the next data version from its own cache, so the old holder and the new holder both wrote version N+1 under the same key (<type>/<modelId>/<dataName>/<N+1>/). The later upload overwrote the earlier one. Both runs exited 0 and one acknowledged write was lost.
The overwrite happens inside the extensions' sync. pushChanged, preparePush and commitPush upload with a plain put, so the last writer wins.
What swamp-club#3373 does in core
Before each push under per-model locks, core re-reads each lock and compares it with its reading at acquire: holder, hostname, pid and nonce. If the lock was lost, core refuses the push and fails with model_lock_lost.
That narrows the window but cannot close it. The check and the upload are not atomic, so a breakglass and a takeover that land between them can still lose a write. Phase 5's model leases close it in the long run.
Proposal
Make the S3 and GCS sync services write data version files create-only:
- S3: PutObject with
If-None-Match: *. - GCS:
ifGenerationMatch=0.
That covers the files under <type>/<modelId>/<dataName>/<version>/. A second write of a version that already exists then fails instead of overwriting, whatever happened to the locks.
Things to settle
- Retries and re-publishes. The publish-and-verify loop in core (swamp-club#3146) re-stages the same version. A precondition failure where the remote bytes equal the local bytes must count as success, not as an error.
- Which keys stay overwrite-in-place. The
latestmarker, the index and other mutable files must keep their current behaviour; only immutable version files become create-only. - Endpoints without conditional writes. Some S3-compatible endpoints don't support
If-None-Match. S3Lock already falls back forIf-Match; this needs a matching fallback and a warning. - The in-memory remote fake. The fake in
packages/testing/in_memory_remote.ts(swamp repo) should model the new semantics so core tests can pin them. - What the losing writer sees. It should fail with a clear error naming the version that already exists.
Open
No activity in this phase yet.
system commented 10/13/2026, 6:00:38 PM
Classified automatically when this issue was filed.
- Source: Extensions
If you feel this classification is incorrect, add a ripple to tell us so.
Sign in to post a ripple.