Repository navigation
CAS: concurrent ATTACH PARTITION / INSERT loses the 90s ref_catalog/_ckpt policy (Code 210); common-pool num_tries then 300s-backs off REPLACE_RANGE #2343
Description
✅ I checked the Altinity Stable Builds lifecycle table, and the Altinity Stable Build version I'm using is still supported.
Type of problem
Bug report - something's broken
Describe the situation
Under the concurrent /alter/attach partition combinatorics, CAS cannot finish ordinary writes. A 10-row INSERT and ALTER TABLE … ATTACH PARTITION … FROM fail with Code: 210 NETWORK_ERROR after the CAS write policy gives up on the pool-global cas/ref_catalog object or a per-namespace _ckpt. That is a user-visible failure on MergeTree as well as ReplicatedMergeTree — no replica handshake is required.
On replicated destinations the same load also ages REPLACE_RANGE queue entries that never ran: selectQueueEntry increments num_tries before trySchedule, a saturated background_common_pool (default 8, max_tasks == max_threads, no wait queue) refuses without rolling the counter back, and the first real CAS error then hits the 300 s max_postpone_time_for_failed_replicated_tasks_ms cap. Replicas miss rows; SYSTEM SYNC REPLICA loses to that budget.
This is not #2310. #2310 is the later relink-confirm Unknown → NO_REPLICA_HAS_PART path (part 1 replica divergence). This issue is the control-plane write that dies first on part 3, including on non-replicated tables. They compose: catalog timeouts leave the ledger recovering, which feeds #2310.
Discovered on PR #2300 cas_s3_cache_alter_attach_3 (job), SHA f377ba3a499beacf5d032f3d34a69185765b8699, package 26.6.4.20001.altinityantalya. Same class on every CAS attach part-3 shard of that run (x86/arm × --cas / --cas-s3-cache, attempts 1 and 2 = 8/8). Isolated /alter/attach partition/part 1/* and a 63-scenario subset pass; the full part-3 Pool() is what saturates the catalog.
This issue:
- Fails user
INSERT/ATTACHwithNETWORK_ERRORafter a 90 s CAS policy, not a network fault - Is CAS-only and load-sensitive. Local-disk attach part 3 is not in this PR’s job set; isolated CAS runs pass
- Is not unique to CAS improvements #2300 — same attach-load class already failed on the CAS baseline (R7). CAS improvements #2300 still owns making antalya-26.6 CAS attach reliable
- Must not be xfailed. The assertion is correct
How to reproduce the behavior
Environment
- Version:
26.6.4.20001.altinityantalya(PR CAS improvements #2300 headf377ba3) - Build type: release (Altinity Antalya)
- Arch: x86_64 and aarch64 (both fail)
- Flags:
--use-keeper --with-analyzer - Storage:
--casor--cas-s3-cache(storage_policy = 'cas_policy', shared pool)
CI combinatorics (reliably hits it)
From altinity/clickhouse-regression/alter. Part 3 runs seven features in one Pool(), and simple attach is @Repeat(100) with an inner Pool(6):
./regression.py --local --with-analyzer \
--clickhouse-binary-path docker://<pr-2300-package> \
--cas \
--only '/alter/attach partition/part 3/*'--cas-s3-cache fails the same way. --only of a single failed leaf does not reproduce — the catalog needs the concurrent CREATE/INSERT/ATTACH storm.
On this SHA, /alter/attach partition/part 1/* and part 2/* passed on all four CAS attach jobs. Part 3 is the reliable trigger.
Local measurements (CAS author, part 1 full set, ~4 min)
These are the amplifier, not the part-3 INSERT failure:
REPLACE_RANGE/DROP_RANGErun in the common background pool- On CAS, one
REPLACE_RANGEis ~1.4 s p50 (mean 1.73 s) - Suite keeps ~120 replicated tables in flight; pool ~95 % busy
- Queue rows wait minutes (20+ s typical, up to 214 rows/node)
- 116 / 1006 queue rows had
num_tries ≥ 19, emptylast_exception,num_postponed = 0(max 58) — never executed, only refused admission - First real error then gets the 300 s backoff immediately
Expected behavior
- A 10-row
INSERTinto a CAS MergeTree table succeeds.ATTACH PARTITION FROMon the same disk succeeds. - CAS control-plane contention (
cas/ref_catalogtoken-CAS,_ckptRMW) is queued or retried inside the write, or surfaced as a retryable error the client/test can wait out — not a 90 s hard fail mapped toNETWORK_ERROR. - A replication-queue entry that was never scheduled must not increment
num_tries. The first real exception on that entry must start backoff from try 1, not from a counter already at 19–58. - After attach, every replica has the source partition’s rows (
RQ.SRS-048.CAS.Relink.AttachPartitionFrom).
Actual behavior
On release builds (26.6.4.20001.altinityantalya, #2300 f377ba3)
This job (cas_s3_cache_alter_attach_3), first leaf:
/alter/attach partition/part 3/check simple attach partition 2/simple attach partition/run #9/partition key _intDiv_a_2__intDiv_b_2__ tables partitioned_MergeTree empty_partitioned_MergeTree
Code: 210. DB::Exception: Received from localhost:9000. DB::Exception: CAS write could not be committed (read of 'data/cas/ref_catalog': gave up at the policy deadline after 13 attempt(s)); retrying later. (NETWORK_ERROR)
(query: INSERT INTO source_41bbc7e0_acde_11f1_92d3_9200088b5af9 (p, i) SELECT 3, rand64() FROM numbers(10))
Same burst (~06:11–06:12 UTC) also failed rbac/pattern #159 and two partition key ReplicatedMergeTree combinations (TestFlows only prints the first traceback).
Sibling part-3 job (aarch64 cas_s3_cache_alter_attach_3):
Code: 210. DB::Exception: CAS write could not be committed (CAS _ckpt for namespace 'alter-cas-clickhouse1/store/adf/adf9421d-…@cas@': persistent CAS contention, the checkpoint contribution was not published); retrying later. (NETWORK_ERROR)
(query: ALTER TABLE destination_14b532f0_… ATTACH PARTITION 3 FROM source_14b5327a_…)
No crash, no sanitizer, no server death. Move/replace CAS jobs on the same SHA are green.
Consistency on this SHA
| Job | part 1 | part 2 | part 3 | move | replace |
|---|---|---|---|---|---|
| CAS x86 / arm | OK | OK | Fail ×2 | OK | OK |
| CAS S3 cache x86 / arm | OK | OK | Fail ×2 | OK | OK |
8/8 part-3 CAS jobs failed. Different leaves each time (run #0 vs #9, different keys/engines).
Root cause analysis
Two defects compose. Line numbers are #2300 f377ba3 (altinity/feature/antalya-26.6/CAS-improvements), read from a local clone after fetching that branch. Do not use the older #2310 source map (684161d).
Defect 1 — CAS control-plane write loses a 90 s policy (this job)
cas/ref_catalog is one object for the whole pool (spec INV-3):
/// The whole-pool namespace catalog (spec INV-3): one object, key `cas/ref_catalog`
/// (`Layout::refCatalogKey`), read on every fold round and every recovery, mutated by one token-CAS
/// write per lifecycle transition.Key: prefix + "/cas/ref_catalog" (CasLayout.h refCatalogKey()). Every namespace CREATE/recovery/fold re-GETs it. Concurrent attach combinatorics (many tables, many namespaces) turns that object into a hotspot.
Default write policy is 90 s (CasRetry.h):
/// `within(90'000)` -- the default write policy.
static Retry standard() { return within(90'000); }When the read/RMW loop hits that bound it throws NETWORK_ERROR:
void throwCasWriteRetryLater(const String & why)
{
logCasWriteRetryLater(why);
throw Exception(ErrorCodes::NETWORK_ERROR, "CAS write could not be committed ({}); retrying later", why);
}void CasOperation::giveUpReadDeadline(...)
{
last_read_stop = ReadStop::PolicyExhausted;
throwCasWriteRetryLater(fmt::format("{} of '{}': gave up at the {} deadline after {} attempt(s)",
verb, subject, bound.lease_bound ? "lease" : "policy", attempts_made));
}That is the exact job-log string: read of 'data/cas/ref_catalog': gave up at the policy deadline after 13 attempt(s).
The sibling ATTACH failure is the same class on the per-namespace checkpoint. After readModifyWrite returns GaveUp (not FenceLost):
throwCasWriteRetryLater("CAS _ckpt for namespace '" + life.ns.string()
+ "': persistent CAS contention, the checkpoint contribution was not published");The comment on throwCasWriteRetryLater says the condition is “expected to self-heal (the caller retries)”. The caller here is a user INSERT / ATTACH with no retry. The test (and a client) sees a hard error.
Defect 2 — num_tries increments on an entry that was never scheduled (amplifier)
ATTACH PARTITION FROM on ReplicatedMergeTree is a dummy-range REPLACE_RANGE (StorageReplicatedMergeTree.cpp ~457–460). That type is not fetch and not merge/mutate, so it goes to the common executor:
assignee.scheduleCommonTask(
std::make_shared<ExecutableLambdaAdapter>(
[this, selected_entry]() mutable { return processQueueEntry(selected_entry); }, ...),
/* need_trigger */ true);
return true; // return value of scheduleCommonTask is ignoredCommon pool: max_threads == max_tasks == background_common_pool_size (default 8). Unlike merge/mutate, there is no extra task slot.
shared->common_executor = std::make_shared<OrdinaryBackgroundExecutor>(
ThreadName::MERGETREE_COMMON,
background_common_pool_size,
background_common_pool_size, // max_tasks == max_threads
...
);trySchedule refuses immediately when value >= max_tasks_count (MergeTreeBackgroundExecutor.cpp ~161–171). No queue.
Order that ages a never-run entry:
selectQueueEntry()→selectEntryToProcess()constructsCurrentlyExecuting, which does++entry->num_tries(ReplicatedMergeTreeQueue.cpp:2019) and setscurrently_executing.scheduleCommonTask→trySchedule. If the pool is full this returnsfalse. TheExecutableLambdaAdapteris destroyed;~CurrentlyExecutingclearscurrently_executingand future-part tags but does not decrementnum_tries(ReplicatedMergeTreeQueue.cpp:2077–2111).scheduleDataProcessingJobstillreturn true.- Backoff (
getPostponeTimeMsForEntry) is1 << num_triesms, capped atmax_postpone_time_for_failed_replicated_tasks_ms= 300 s, but only afterlast_exception_time_msis set. Schedule refusals leavelast_exceptionempty, so the entry is immediately eligible again and the counter keeps climbing (author: 116/1006 rows,num_tries ≥ 19, empty exception,num_postponed = 0). - The first real failure (Defect 1, or CAS relink confirm returns
UnknownafterATTACH PARTITION FROM; replicas never receive attached parts #2310 confirmUnknown) therefore starts at try 19–58 and is postponed for the full 300 s.SYNC REPLICAin the suite is also 300 s.
How this relates to #2310
#2310 is still correct and still open: confirm Unknown is collapsed with No, taxonomy row 3 throws NO_REPLICA_HAS_PART and forbids byte-fetch (DataPartsExchange.cpp:1632–1636, ContentAddressedExchange.h:19–22). That is the first real error once the queue finally runs.
This issue is why the ledger is not quiescent (catalog/_ckpt timeouts → recovery) and why that first error is immediately unrecoverable inside the 300 s test budget (Defect 2). Fixing only #2310 leaves part-3 INSERT/ATTACH red. Fixing only the pool size leaves confirm Unknown and the num_tries accounting bug.
Suggested fix
Do all three. Raising background_common_pool_size to 16–32, or cutting suite parallelism, is a CI workaround only.
A. Catalog / _ckpt must not fail a user write in 90 s (this issue)
- Serialize writers of
cas/ref_catalog(single in-process mutex / admission), or give the catalog its own longer/unbounded policy distinct from blob PUTs - On
_ckptGaveUp(contention, not fence): retry under the caller’s lease, or return a value the attach/insert path can wait on — do not throwNETWORK_ERRORto the client - Do not map “policy exhausted on a shared control object” to
NETWORK_ERRORfor a user query; that code is the replication-queue retry class and is the wrong signal here
B. Do not increment num_tries until the task is admitted (this issue, also worth upstream)
- Increment
num_triesinprocessQueueEntry/executeLogEntryaftertrySchedulesucceeded, or decrement in~CurrentlyExecutingwhenfuncnever ran - Honour
scheduleCommonTask’sfalseinscheduleDataProcessingJob(today it is ignored) - Optionally: give
REPLACE_RANGEthe fetch pool, or give the common executormax_tasks > max_threadslike merge/mutate
C. Confirm Unknown ≠ “source in doubt” (#2310)
Already specified there: split No / Unknown on the wire, or byte-fetch when gate 0 still has the part.
Do not
- Xfail
/alter/attach partition/… - Only lengthen the test timeout
- Call Defect 1 “the test is too aggressive”. Isolated runs pass; a 10-row INSERT must survive concurrent attaches on the same pool
Tests that must go green
cas_alter_attach_3andcas_s3_cache_alter_attach_3(x86 + aarch64) on this package- The first leaf of this job: MergeTree → MergeTree
simple attachtuple-key INSERT - Existing
cas/tests/replicated.pyreplicated_attach_partition_from - CAS relink confirm returns
UnknownafterATTACH PARTITION FROM; replicas never receive attached parts #2310’s confirm-Unknownfollower-convergence test, once filed there
Additional context
CI failure
- PR: #2300 CAS improvements
- Job:
RegressionTestsRelease / CASS3CacheAlter (attach, 3) / cas_s3_cache_alter_attach_3 - Run: 34388936376 attempt 2 (attempt 1 failed the same four part-3 jobs)
- Job log: https://github.com/Altinity/ClickHouse/actions/runs/34388936376/job/102757852231?pr=2300
- Siblings (same SHA, same class):
- cas_alter_attach_3 x86
- cas_s3_cache_alter_attach_3 arm (
_ckptcontention on ATTACH) - cas_alter_attach_3 arm
- Commit:
f377ba3a499beacf5d032f3d34a69185765b8699 - Package:
26.6.4.20001.altinityantalya - CI report: https://s3.amazonaws.com/altinity-build-artifacts/PRs/2300/f377ba3a499beacf5d032f3d34a69185765b8699/34388936376/ci_run_report.html
- Base:
antalya-26.6
Related issues
- #2310 — relink confirm
Unknownafter ATTACH (part 1 replica divergence). Keep that issue. This one is the write-deadline +num_triespair that part 3 hits first. - #2173 — cross-disk
tmp_replace_from_*unique-ref (different path) - #2233 — relink
NETWORK_ERRORstorm on soak (related load) - #2031 — static audit; CAS-112 is the uncached
ref_catalogGET on every positive ref-log append
Requirements
RQ.SRS-048.CAS.MergeTree.InsertSelect / Transparency
A 10-row INSERT on cas_policy must succeed.
RQ.SRS-048.CAS.Relink.AttachPartitionFrom
ATTACH PARTITION FROM SHALL preserve correct logical partition contents
on all replicas.
RQ.SRS-048.CAS.Relink.CrossPool.ByteFallback
SHALL fall back to byte fetch when confirmation is unavailable.
Scope (do not file one issue per leaf)
All unexpected Fail leaves on the four part-3 CAS jobs of run 34388936376 are this class: simple attach (tuple keys, @Repeat(100)), partition key / partition key datetime, rbac. Ancestor Fail rows are not separate bugs.
Workarounds (CI only)
- Raise
background_common_pool_size2–4× (8 → 16 or 32) - Cut intra-part TestFlows
Pool()concurrency /Repeat(100) - Neither fixes Defect 1 on a 10-row INSERT once the catalog is hot
Activity
filimonov commentedon Sep 10, 2026
Part 1 is build on assumptions, and is (most probably) incorrect.
Part 2 is real defect of the upstream code.
Both are not release blockers, let's try
background_common_pool_size=32and/or reduce test concurrencyfilimonov commentedon Sep 14, 2026
Part 2, restated:
num_triesis charged before the entry is admitted to a poolWhere.
ReplicatedMergeTreeQueue::CurrentlyExecuting::CurrentlyExecuting(ReplicatedMergeTreeQueue.cpp:2019) does++entry->num_trieswhenselectQueueEntrypicks the entry.StorageReplicatedMergeTree::scheduleDataProcessingJob(StorageReplicatedMergeTree.cpp:4430) then callsassignee.scheduleCommonTask, which istryScheduleon the common executor. When the pool is full (background_common_pool_size, default 8,max_tasks == max_threads, no wait queue)trySchedulereturnsfalse, theSelectedEntryis destroyed, the entry goes back to the queue, and the increment stays.scheduleFetchTaskandscheduleMergeMutateTaskhave the same shape for the fetch and merge pools.Effect.
ReplicatedMergeTreeQueue::getPostponeTime(:1637) computes the backoff after a failure as2^num_triesms, capped bymax_postpone_time_for_failed_replicated_tasks_ms(300 s). An entry that was refused admission 19 times and never ran hasnum_tries = 19; its first real exception postpones it for the full 300 s instead of 2 ms. Measured oncas_alter_attach_1(run 3, x86): 116 of 1006 queue rows hadnum_tries >= 19, emptylast_exception,num_postponed = 0. The queue is also slow without any exception: onaarch64 cas_alter_attach_3(run 10) replicas diverged 2700 times with zero CAS exceptions in the logs, purely from waiting on the 8-slot pool where a CASREPLACE_RANGEtakes about 1.4 s.Scope. Not CAS-specific. The code is identical in upstream
26.6andmaster(ReplicatedMergeTreeQueue.cpp:2052and:1649on master). CAS only makesREPLACE_RANGEslow enough to saturate the default pool. The regression-suite change (background_common_pool_size=32, Altinity/clickhouse-regression 52e8775ae6) hides it on the CI stand; a server with the default pool and heavy partition operations still hits it.Fix. Charge
num_triesonly when the task actually starts, or roll it back whentrySchedulerefuses. Either:scheduleDataProcessingJob, onscheduleCommonTask/scheduleFetchTask/scheduleMergeMutateTaskreturningfalse, decrementnum_triesand clearlast_attempt_time(three call sites, no behavior change otherwise); or++num_triesandlast_attempt_timefrom theCurrentlyExecutingconstructor intoprocessQueueEntry/ the merge and mutate task entry points, so refusals never touch the counter.The first is smaller and keeps
CurrentlyExecutingas the single owner of the executing state. A unit test can drive it: select an entry with a full pool, observenum_tries == 0after the refusal, then fail the entry once and check the postpone time is2 ms, not300 s.ps. this can be reported as a separate issue (also in the upstream)
filimonov commentedon Sep 14, 2026
Part 1
Verification pending. No CI run has exercised attach part 3 with the 32-thread pool yet (the regression change landed after run 10 attempt 2 started), and MasterCI carries no regression jobs.
alsugiliazova commentedon Sep 24, 2026
@filimonov Here is failed run of attach part 3 with the 32-thread pool https://github.com/Altinity/clickhouse-regression/actions/runs/35472736745/job/105976572282 and another one https://github.com/Altinity/clickhouse-regression/actions/runs/34722153395/job/104410417483
alsugiliazova commentedon Sep 25, 2026
Upstream issue for part 2 ClickHouse#122106
alsugiliazova commentedon Sep 25, 2026
What remains after the upstream
num_triesfixClickHouse/ClickHouse#122106 fixes the replication-queue amplifier. It will stop rejected executor admissions from turning the first real failure into a 300-second backoff. It will not stop the CAS failures that start this sequence.
The two linked runs already use
background_common_pool_size=32, yet attach part 3 still fails. The newer run gives two particularly useful signatures:The same failure window also contains policy exhaustion on manifest GETs and
data/cas/ns/stream/LIST operations. This means the initial event is broader object-store unavailability, not just a conditional-write conflict onref_catalog. The CAS control plane then amplifies that event.1. Remove the pool-global catalog bottleneck
ref_catalogis one object for the whole pool (CasLayout.h:460-465). Every namespace lifecycle mutation decodes, copies, re-encodes, and conditionally replaces the complete object (CasRefCatalog.cpp:154-203). Namespace birth performs a catalog read, aCreatingcatalog update,_ckptcreation, aLivecatalog update, and another catalog read (CasRefLedger.cpp:1297-1347,CasRefCatalog.cpp:600-710).CasHotKeysalready serializes same-process catalog writers and caches the latest body. Therefore the problem is not an uncontrolled same-process 412 race. The remaining problems are:CasRefCatalog::readbypasses the hot-key cache;CasHotKeys.cpp:62-142).The
zero attempt(s)failure proves head-of-line blocking: that update spent its complete budget waiting behind another holder and never sent an S3 request.Fix direction: shard catalog ownership by namespace/hash, or move lifecycle state to bounded per-namespace objects. Avoid requiring full-catalog GETs on namespace recovery. A larger timeout or pool only delays the same saturation point.
2. Serialize
_ckptpublishers and report the real failure_ckptis per namespace, butpublishCkptuses genericreadModifyWritedirectly (CasRefCkpt.cpp:195-284). Birth, recovery, append-frontier publication, and snapshot publication can touch the same key independently.The current error is also misleading.
CasRefCkpt.cpp:270-280maps every non-fenceGaveUptopersistent CAS contention. That includes transport/read deadline exhaustion and ambiguous writes, not only ETag conflicts. The log text cannot distinguish contention from an object-store outage.Fix direction: route
_ckptupdates throughCasHotKeysor the namespace append lane. Preserve theGaveUpreason, source, attempt count, and last backend exception instead of replacing all outcomes withpersistent CAS contention.3. Preserve the underlying object-store error
CasRequests::readLoopretries classified backend exceptions, then replaces the final cause with a generic policy-deadline error (CasRequests.h:455-524,CasRequests.cpp:495-500). The artifacts contain no object-store logs, so they cannot tell us whether the trigger was throttling, connection pressure, or backend latency.Add per-operation metrics and logs for:
ref_catalog,_ckpt, manifest, LIST);4. Fix relink confirmation separately
#2310 is a separate protocol problem.
NoandUnknowncollapse to the sameunprovenwire response (DataPartsExchange.cpp:285-313), and the receiver throwsNO_REPLICA_HAS_PARTinstead of byte-fetching (:1624-1635). Catalog recovery and object-store slowdown can increaseUnknown, butUnknowncan also occur without either condition. The attach suite will remain vulnerable until this path has its own fix.Acceptance criteria
Pool/server instances sharing one prefix while creating at least the current suite's namespace count.CREATE,INSERT, orATTACHfailure._ckptpublishers. Verify all contributions survive and diagnostics identify the actual failure class.Until these changes land, reducing attach-suite concurrency is a valid CI workaround. Raising
background_common_pool_sizeis not sufficient, as both linked 32-thread runs demonstrate.filimonov commentedon Sep 25, 2026
Re-read both linked "pool=32" runs with the artifacts.
Run 34722153395 (Sep 15) is package
26.6.2, before #2300. It fails with a rawCode: 499 S3_ERROR ... Poco Timeout ... key data/cas/ref_catalog, object size 172391onCREATE TABLE ON CLUSTER. It does not say anything about26.6.4.Run 35472736745 (Sep 20) is
26.6.4withbackground_common_pool_size=32. 1 of 4 part-3 CAS jobs failed (x86--cas), the rerun was green. All 6 failed leaves are one event, 02:06–02:08 UTC, TestFlows printed only one traceback:AWSClient: Failed to make request to: http://minio:9001/... Poco::TimeoutExceptionevery 5 s, 13 attempts = the 90 s policy. Bursts per minute onclickhouse2: 515, 264, 213 timeouts.ref_catalog, manifest GETs and background merges alike, so the store was slow as a whole, not one key.gave up ... after zero attempt(s)is the catalog lane waiting behind a holder that was itself stuck in the same timeouts._ckpt"persistent CAS contention" ATTACH failure is the same minute. The message is misleading: any non-fence give-up is reported as contention.So on
26.6.4the remaining failure is "S3 is down for longer than the retry budget". Retries exist everywhere, but they cannot help if the store is down for 90 s. Any test on any storage fails in that case.The catalog hot-path story is true only in part.
ref_cataloggrew from 89 KB to 1.37 MB in 2.5 hours (Removingrows are pruned only by GC), each namespace birth is 3 GETs + 2 conditional PUTs, andclickhouse2alone lost 6796 conditional catalog PUTs with 412 during the run. But this load comes from the test creating and dropping thousands of tables. Table churn at this rate is not a normal workload.What we cannot answer today: why RustFS stalled. Its logs are not collected. RustFS writes to
/logsinside the container, sodocker logs(_service_logs/minio.log) has 3 lines. And the report prefix in the bucket is overwritten by the rerun, the failed attempt's service logs are gone.Proposed next steps on the infra side:
RUSTFS_OBS_USE_STDOUTand the other RustFS knobs from CI: cap the RustFS blocking-thread pool on the CAS-S3 lanes and collect its log #2384, same as the stateless CI lanes.1.0.0-beta.12to1.0.0(https://github.com/rustfs/rustfs/releases/tag/1.0.0).The
num_triesamplifier stays with ClickHouse#122106.filimonov commentedon Oct 15, 2026
New data from CI runs on 2026-10-08 … 10-15. The
_ckptfailures are the same RustFS stall as above. They are not contention.What happens
CAS write could not be committed (_ckpt persistent CAS contention)is misleading:publishCkptprints it for any give-up. In the failure windows there are zero 412 responses on_ckpt, and the failing call never sent its PUT; it was stuck on the first GET. The "policy deadline after N attempt(s)" variant (e.g. Never resend a CAS blob's object-creating S3 request #2555) is the same event, printed by another code path.How often
1.0.0-beta.12) has not changed since July.Reproduced locally: a proxy that holds only GETs to
_ckptgives exactly this error onINSERT. Without the stall, 200 concurrentATTACH PARTITIONgive 0 errors.Next
1.0.0instead ofbeta.12._ckptmessage will say the real reason (deadline vs. fence), PR to follow.