Relationships
#2315 gcs-datastore: verify generation-precondition support at setup and in doctor; an emulator or proxy that ignores ifGenerationMatch makes locking silently unsafe
Opened by hammz · 9/21/2026· Shipped 9/21/2026
Problem
Same class of gap as #2300, on the GCS side. The GCS datastore's correctness
rests on generation preconditions: lock acquisition is an upload with
ifGenerationMatch=0 (gcs_client.ts putObjectConditional), and the
shard-first index merge uses ifGenerationMatch=<generation>. On a GCS-API
endpoint that silently ignores those preconditions (returns 200 instead of
412), putObjectConditional returns a generation for every caller: every
writer "acquires" the lock, and index merges lose updates. Nothing reports it.
Today nothing checks for it: GcsDatastoreVerifier.verify()
(datastore/gcs/extensions/datastores/_lib/gcs_verifier.ts) issues
bucketExists() only, so swamp datastore setup, swamp datastore status
and swamp doctor datastores are all green on such an endpoint.
Real GCS honours preconditions. The exposure is emulators and proxies:
fake-gcs-server (which datastore/benchmarks already runs against), the GCS
JSON-compatible fronts some object stores ship, and anything sitting between
swamp and the bucket.
Proposed solution
Port the #2300 probe. Under a unique probe key beneath the already-filtered
_control/ prefix:
- upload with
ifGenerationMatch=0-> expect success, note the generation - the same request again -> expect
PreconditionFailedError(412) - upload with a deliberately stale
ifGenerationMatch-> expect 412 - delete the probe key, best-effort
Report details.conditionalWrites as supported / ignored /
write-denied / inconclusive and fail setup (or report unhealthy) when step
2 or 3 returns 200: "this endpoint ignores generation preconditions;
distributed locking would not be safe."
Follow #2300's shape so the two datastores stay comparable:
- distinguish a write rejection (403) from an ignored precondition, so narrow-IAM users are not sent hunting for a compatibility bug
- memoize the verdict per process, keyed on endpoint/bucket/prefix, with a
short TTL -
swamp servestreams health on a 1s-floored interval and reachesverify(), so an unmemoized probe becomes sustained write traffic - make the cleanup delete best-effort so it cannot flip a working endpoint to
unhealthy; surface a failed cleanup as
details.probeCleanup
No swamp-core changes needed: setup, status, doctor and the serve admin
handler all already call verify(), and setup already aborts on
healthy: false.
Alternatives considered
Leaving it to the S3 probe alone. Doesn't help - the two extensions share no verifier code, and a GCS user gets no signal at all today.
Related: #2300, which adds the equivalent probe to @swamp/s3-datastore and
is the reference implementation for this one.
Upstream repository: https://github.com/systeminit/swamp-extensions
Environment
- Extension:
@swamp/gcs-datastore@2026.09.17.2 - Discovered while implementing #2300; deliberately kept out of that PR's scope.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.