fix: serialize whole-record bucket metadata updates - #103
Merged
Conversation
Pause one whole-record writer at the metadata PutObject boundary and let a different config writer commit from the same stale snapshot. Assert that policy+CORS and tagging+SSE both survive on disk and in the resident cache. Signed-off-by: Feng Ruohang <rh@vonng.com>
Use one per-bucket metadata.lock for ordinary updates, CORS transitions, and legacy bulk replication. Persist and update the local cache while locked, then release before peer metadata reload fan-out.\n\nRefs: #102 Signed-off-by: Feng Ruohang <rh@vonng.com>
Create policy and CORS state, force-create the existing bucket, and require the original Created time and both metadata fields to survive. Signed-off-by: Feng Ruohang <rh@vonng.com>
After storage bucket creation, merge required versioning and lock defaults into the latest on-disk metadata under metadata.lock. Avoid ForceCreate and site-adoption rewrites that replaced existing bucket configuration.\n\nRefs: #102 Signed-off-by: Feng Ruohang <rh@vonng.com>
Apply only validated import fields to a fresh locked record, block ForceCreate after real read errors, and route legacy or target-config migration saves through the shared lock. Compute lifecycle deletion state from the locked record.\n\nRefs: #102 Signed-off-by: Feng Ruohang <rh@vonng.com>
Preserve existing records only for ForceCreate, reject ghost metadata on genuine creation, keep object-lock versioning invariants, and complete metadata saves after caller cancellation. Expand deterministic coverage for peer bulk, lifecycle delete, ghost creation, and cancellation.\n\nRefs: #102 Signed-off-by: Feng Ruohang <rh@vonng.com>
Release metadata.lock with defer and dispatch only the configuration types actually present in the import. Avoid stamping unrelated peer metadata with the import timestamp.\n\nRefs: #102 Signed-off-by: Feng Ruohang <rh@vonng.com>
Use a short dedicated migration lock timeout and fall back to the already loaded legacy or target configuration in memory when persistence is contended. Never fall back to an unlocked metadata save, and keep peer fan-out contexts free of stale lock markers.\n\nRefs: #102 Signed-off-by: Feng Ruohang <rh@vonng.com>
State that old cors-config.lock and new metadata.lock do not interoperate during a rolling upgrade, while the unchanged on-disk format remains rollback-compatible. Signed-off-by: Feng Ruohang <rh@vonng.com>
Degrade to the loaded in-memory configuration only when the short migration lock cannot be acquired. Preserve parse, I/O, KMS, and persistence errors once the lock is held. Signed-off-by: Feng Ruohang <rh@vonng.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
Vonng
force-pushed
the
codex/issue-102-metadata-lock
branch
from
September 1, 2026 23:21
7751d7a to
53b09f4
Compare
This was referenced Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contribution Licensing (no CLA, inbound=outbound, DCO required)
Every commit carries a DCO
Signed-off-bytrailer. The red-test commits intentionally precede their fixes so the reproduced failures remain auditable in history.Description
Introduce one distributed namespace lock for each real bucket metadata record:
The PR serializes the authoritative disk read, mutation,
.metadata.binsave, and local cache publication for:BucketMetadataSys.Update/Delete;Peer metadata reload fan-out and
BucketMetaHookdispatch happen only after the lock is released.BucketMetadata.Saveand the.metadata.binobject lock remain lock-free at this layer to avoid non-reentrant self-deadlock.The PR also:
ForceCreatefrom replacing an existing bucket's policy, CORS, tags, SSE, timestamps, and other fields;Refs #102.
Motivation and Context
.metadata.binis one whole-record object. Previously, different config writers could read the same old record and save different one-field changes. The last save silently discarded the other field. CORS had a separatecors-config.lock, which serialized CORS with itself but not with policy, tagging, SSE, import, migration, or bucket creation.The deterministic red tests demonstrate both policy overwriting CORS and tagging overwriting SSE on disk and in the resident cache. A separate red test demonstrates
MakeBucket{ForceCreate:true}replacingCreated, policy, and CORS with a fresh record.How to test this PR?
Passed locally on macOS/arm64 with Go 1.27.0:
-race;go build ./...;go vet ./cmd;go test ./cmd -count=1(final run: 128.758s).Claude Code Opus 5 performed two adversarial implementation reviews. The first found one P0 and two P1 MakeBucket edge cases, all corrected with dedicated regressions. Final verdict: GO, no P0/P1; remaining P2 items are documented tradeoffs or deferred #102 follow-ups.
Compatibility impact
.metadata.binschema, object format, or API response format changes.<bucket>.lck -> metadata.lock -> .metadata.bin.cors-config.lock; new nodes usemetadata.lock. The serialization guarantee is therefore complete only after every node in a cluster is upgraded. Avoid bucket-metadata writes during the rolling window.ForceCreatepreserves valid existing metadata; corrupt/future-version metadata fails loudly rather than being silently overwritten.metadata.lock; if the lock is unavailable, reads use the already loaded configuration in memory and retry persistence later. Real parse, I/O, KMS, and save errors still surface.Deliberately deferred under #102:
Types of changes
Checklist:
git commit -s) per the DCOmake verifierspassespgsty/silo.pgsty.com