Repository navigation
Conversation
Two processes of one server_uuid could adopt the same writer epoch: when our counter write timed out and a twin wrote the same next value, the resolve read found identical bytes and reported our write as landed. Each allocation decision now writes a fresh random `allocation_nonce`, so only our own write matches our bytes. The field is a plain key that 26.6.4 readers skip. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every owner write sets `owner_life` to the read value + 1, so no two owner writes carry the same bytes and a delayed If-Match cannot land after a later write; `retirement_verified` records a finished decommission. Both are optional keys that 26.6.4 readers skip. Every claimer re-reads the owner after its mount write and requires its own uuid and life; otherwise it fences its own body and fails, so a successor does not wait the lease out. A remount that finds another life stops for good. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decommission no longer deletes owner, epoch or mount: the counter keeps epochs from repeating across a member's lives, and the farewell body keeps a paused claimer from minting a lower epoch into an absent slot. Each run tombstones the owner right after its claim, bounded by its lease, confirms the claim, drains on its fenced mount plane, writes its farewell and marks the retirement verified. A lost claim rolls the tombstone back unless an earlier run verified it. After an unresolved tombstone the admin withholds its farewell, so the victim does not run before a late tombstone lands. The result column slot_removed becomes member_retired and namespaces_pending_gc is appended. The admin refuses a member without an epoch counter instead of minting one, and a mount that skips the capability probe now logs it. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mount's startup sweep deleted every staging object under its prefix, including a successor's in-flight uploads when two processes of one member overlapped. Keys are now staging/<srid>/<writer_epoch>/<random>.tmp and the sweep deletes only epochs below the live one; flat keys count as epoch 0 and a nested member's keys are left alone. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A decommission keeps the epoch counter, so allocateWriterEpoch never mints over an absent counter for it and EpochMintPolicy goes. The vanished-slot pacing and the observation prune stay, because a 26.6.4 decommission or a hand delete still removes a mount slot. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MountLeaseRenewer::renewOn sampled the stop flag before the write. A stop that landed after the sample refused the admission with nothing sent, yet the renewal went terminal and finishTeardown skipped the farewell. Read the flag after the refusal instead. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a rejoin under a new server_uuid the slot can hold the previous uuid's farewell or self-fence. claimMount now reclaims such a terminal body below its epoch once a fresh owner read shows the claimer's uuid and life, not retired. A start and a self-remount pass that expectation; the decommission admin does not. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A terminated or fenced slot whose owner is tombstoned reads as `retired`, and the new last column `retirement_verified` says whether a decommission verified it. A terminal row whose owner does not decode reads as `corrupt`. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A start over a verified retirement whose data is gone takes the owner at the next life under its own server_uuid, same or new, and continues the kept epoch counter. A new uuid rewrites the mount body before the owner. An unverified tombstone, a held slot, leftover data or missing control objects refuse with the action to take. Remounts never rejoin, and a start without a server uuid does not rejoin. Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
filimonov
force-pushed
the
fix/antalya-26.6/cas-member-lifecycle-pr-c
branch
from
October 17, 2026 08:34
47bb142 to
e203482
Compare
This branch has not been deployed
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.
A CAS server whose
server_root_idwas decommissioned withSYSTEM CAS DROP POOL MEMBERnow rejoins the pool as an empty member on its next start, with the same or a new server uuid. Before, the id was refused for good and needed a hand-edited owner object or a newserver_root_id.Built on PR B; until it merges, this diff also shows PR B's commits. Base:
antalya-26.6-20261008.Related: #2567
Changes
Pool::openover a tombstoned owner takes the id over whenepochandmountexist, the retirement is verified, the slot is terminal and the old life's data is gone. The owner moves to a new life byIf-Match, the epoch counter continues, and the member starts empty. A conflict re-reads the owner and decides again. The rejoin logs a warning with the old and new uuid and life.epochormount(an id retired by 26.6.4, or deleted by hand; checked first, so such an id is never sent to a rerun), an unverified tombstone, a held slot, data of the old life, and an owner or slot that changes on every decision.ForeignOwner, and the decommission tool never takes a foreign body.roots/<srid>/clickhouse_access_check_<uuid>key. Any other object there still blocks the rejoin.previous life remainsnames the part left and, for catalog rows and manifests,SYSTEM CAS GC RUN '<disk>'on the GC leader. The foreign-owner refusal also names a duplicateserver_root_idbetween two running servers.system.cas_mountsshows a decommissioned member asretired(one moreGETper terminated or fenced row) and adds the last columnretirement_verified: 1 once the retirement is verified, else 0. A rerun ofSYSTEM CAS DROP POOL MEMBERis forretiredwithretirement_verified = 0only.system.cas_mounts,SYSTEM CAS DROP POOL MEMBER). Decommission is no longer called permanent: the data is gone for good, the id is not.Risks / notes
retiringstate: a tombstoned owner's terminal row isretired, andretirement_verified(new last column) tells verified from not. Queries that matchedretiringfrom the earlier revision of this PR must read the column.server_root_id. Its local table definitions attach with no rows.roots/object, leaves a slot the next start refuses, and a decommission rerun cannot clear it (the admin never takes a foreign body). The refusal says what to delete by hand.clickhouse diskswithout a server uuid file does not rejoin: it keeps the refusal.cas_server_root_id. RunSYSTEM CAS DROP POOL MEMBERonly from upgraded servers: a 26.6.4 admin concurrent with a same-uuid rejoin can deleteepochandmountunder it.retiredwithretirement_verified = 0; PR B's fenced tombstone narrows the window.DROP POOL MEMBERof them in that window is refused.Testing
epochandmountis refused with "configure a new<cas_server_root_id>" and no rerun advice;retiredwithretirement_verified0 and 1 inlistMounts; one test over everyServerRootLeftover(control objects unchanged, probe key built fromtoString(UUID)).test_cas_drop_pool_member: after each post-rejoin insert,SYSTEM CAS GC RUNon every node, then the rows are rechecked; a verified retirement readsretiredwithretirement_verified = 1. Module: 3/3.CAS*unit tests: release 2747, ASan 2751, all passed. Every commit builds and passes releaseCAS*.CaEpochAllocCoreround 14): with the tombstone allowed to land until GC fences the admin's slot, a clean farewell after an unresolved tombstone puts it on a running member (NoTombOverLive); with the farewell withheld the late-landing, paused-run and 26.6.4 rows are green under the rerun rule. The default battery (187 rows) meets every expectation.retiredwithretirement_verified = 0and the rerun converges; a kill after the verified mark showsretirement_verified = 1and needs no rerun; every rejoin comes back empty and its new rows survive three GC rounds;dangling = 0after every cycle; soak oraclePHASE3 OK. The one red is theNoWaitrefusal text, which does not name the state to wait for; that text belongs to the base series, not this PR.Commits (on top of PR B)
ba336d2c60a9Let a CAS start reclaim another uuid's terminal body of its own ownerb4ede038732fShow retired CAS members in system.cas_mounts85df7b155084Rejoin a decommissioned CAS server root at server starte20348282ab0Document CAS member rejoin and the retired stateChangelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
A CAS server whose
server_root_idwas decommissioned withSYSTEM CAS DROP POOL MEMBERnow comes back on its next start as an empty member, with the same or a new server uuid, instead of being refused.system.cas_mountsshows a decommissioned member asretired, with a newretirement_verifiedcolumn.Documentation entry for user-facing changes
CI/CD Options
Exclude tests:
Regression jobs to run: