Skip to content

CAS — consolidated static analysis audit findings (tracking) #2031

Description

@vzakaznikov

CAS (metadata_type = content_addressed MergeTree backend) — consolidated audit tracking

This is a tracking issue for a static-analysis audit of the Content-Addressed Storage (CAS) MergeTree
disk backend. It now consolidates 135 distinct findings from a fresh re-run in which all 39 audits were
redone from scratch
against the current PR, each with a unique CAS-### id.

The CAS-### ids are renumbered for this round and do NOT correspond to the previous numbering.
Nothing was carried forward; numeric coincidences with the old catalogue are meaningless. Where an item is
carried over from the previous triage, the old id is referenced inline as prev CAS-###.

Important

This is a static/logical review. Many items are expected to be by-design, not-a-bug, latent, or
already-handled.
The checklist is meant to be triaged item-by-item — please dismiss or resolve freely.

📎 Audit reports

Finding the full detail for a CAS-###

The checklist line is a one-line summary. For the complete write-up, open NEW-FINDINGS.md in the
2026-08-12 gist and search for the id: each entry lists the class, every code anchor, the impact and
the exact trigger, plus a Reported by: line naming the source audit findings (e.g. CAS-012 → ad6-6,
ad6-10). Those ids are prefixed with their report name, so ad6-* lives in
ad6-s3-lifecycle-cross-region.md in the same gist — search that file for the id to get the full
reasoning, the consequence chain and the code walk-through. NEW-FINDINGS.md also ends with a table
mapping each of the 39 reports to the ids it produced, for the reverse direction.

How to triage

For each item, when reviewed:

  1. Check the box once it is triaged (resolved, dismissed, or filed as its own issue).
  2. Replace resolution: inline with a verdict, e.g.
    ✅ fixed (#PR) · 🛠 will-fix · ❌ wontfix · 🚫 not-a-bug · 📐 by-design · 🟡 needs-repro · ↗ split-out (#NNN) · 🔴 still-present.
  3. Add reasoning as a comment referencing the CAS-### id.
  4. Verdicts are pre-filled in this round: items inherited from the previous triage carry that verdict, with the old id and quoted reasoning, and are pre-checked; items previously marked ✅ fixed but found again are flagged ⚠️ was-fixed / still-present and are the priority.

Severity is the highest assigned by any source audit. Class tags:
DATA-LOSS · LEAK · LIVENESS · CONCURRENCY · INTEGRITY · SECURITY · DECODE/DoS · COMPAT · FEATURE-GAP · PERF/SCALE · OBSERV/DAY2 · COMPLIANCE · CONFIG · TEST-GAP · CORRECTNESS.
Grading rule used this round: a fail-closed loud failure grades below silent corruption, and operability, cost
and scale gaps are not High on their own. Resulting counts: 24 graded High, of which 3 are already-accepted
design positions (pre-checked), leaving 21 open High.


🔴 High

  • CAS-001 Shadow/FREEZE and backup namespaces are pool-global while exclusion primitives are per-server-root, so two servers share one unfenced shadow ref table and UNFREEZE on either deletes the other's frozen parts · DATA-LOSS — resolution: 🔴 still-present (carried from prev CAS-070) · evidence strengthened — CA/ContentAddressedMetadataStorage.cpp:897-900
  • CAS-002 Manifest-trust adoptEvidence bypasses the durable condemn marker and EDGE-BEFORE-OBSERVE, so a committed manifest can name a blob GC has already deleted, with no source to re-upload from · DATA-LOSS — resolution: 🔴 still-present (carried from prev CAS-031) · evidence strengthened · anchor relocated — CA/Pool/CasPartWriteTxn.cpp:478-486
  • CAS-003 The GC lease has no TTL, is stealable on differential observation, and the destructive phases are never revalidated, so two GC actors can run destructive phases concurrently on one pool · CONCURRENCY — resolution: 🔴 still-present (carried from prev CAS-032) · evidence strengthened — CA/Gc/CasGc.cpp:3155-3186
  • CAS-004 GC REBUILD has no writer/mount interlock and "read-only" does not gate writes, so a rebuild is accepted on a live writable disk with inserts in flight and the mount census result is discarded · INTEGRITY — resolution: 🔴 still-present (carried from prev CAS-015, which was 🟡 partial) · evidence strengthened — contradicts the partial's premise that "mount-lease should take correctly now" — CA/Gc/CasGc.cpp:2725
  • CAS-005 A repointed committed ref is unrevertible and durable CAS mutations happen before commit() with a silent best-effort rollback, so readers can observe aborted and intermediate states · DATA-LOSS — resolution: 🔴 still-present (carried from prev CAS-021 + prev CAS-097) · evidence strengthened — CA/ContentAddressedTransaction.cpp:280-289
  • CAS-006 Cross-namespace RENAME/moveDirectory is a per-ref non-atomic migration ending in an unconditional source drop with no reconciler, so a crash mid-rename splits the refs and drops any added during the walk · DATA-LOSS — resolution: 🔴 still-present (carried from prev CAS-022 + prev CAS-044) · evidence strengthened — CA/ContentAddressedTransaction.cpp:846-874
  • CAS-007 Nested server_root_id is accepted and victim selection is prefix-based, so SYSTEM CAS DROP POOL MEMBER on srid=a erases the namespaces and control objects of a live member srid=a/b · DATA-LOSS — resolution: 🔴 still-present (carried from prev CAS-064) · evidence strengthened — escalated from collision to nesting, a new destructive consequence — CA/Pool/CasServerRoot.h:104-134
  • CAS-008 Content addressing defaults to a non-cryptographic 128-bit hash and reads never re-verify, so a chosen collision silently substitutes data for every future reader of that content · SECURITY — resolution: 📐 by-design / YAGNI (prev CAS-003 + prev CAS-005 — Filimonov: "selectable hash landed — closes weak-collision concern; will not re-verify hash on read — incompatible with "CH does not slow down""; "S3 has many durability nines and hashes objects itself; CAS will not re-hash on read") — CA/ContentAddressedSettings.cpp:33
  • CAS-009 An occupied content address is admitted on existence alone, and no re-upload, resurrect or staged body is ever re-hashed — every admission is size-checked, never digest-checked · INTEGRITY — resolution: 🔴 still-present (carried from prev CAS-038) · evidence strengthened — partially inherited: the "do not re-verify an already-stored object" half is closed by prev CAS-005 (📐 — Filimonov: "S3 has many durability nines and hashes objects itself; CAS will not re-hash on read"); the scratch/staged-body half remains open — CA/Pool/CasPartWriteTxn.cpp:250-305
  • CAS-010 An empty conditional token turns a fenced write into an unconditional clobber — putOverwrite validates only the token type, so an absent ETag plus an absent-HEAD sends no precondition at all · INTEGRITY — resolution: 🆕 new this round — CA/Backend/CasObjectStorageBackend.cpp:165-173
  • CAS-011 Plain-object writes bypass the request controller and the margin-checked fence and never resolve their indeterminate outcomes, retrying one key up to 100 times with zero backoff · INTEGRITY — resolution: 🆕 new this round — CA/Pool/CasPlainObjects.cpp:21-41
  • CAS-012 Lifecycle rules, Object Lock and storage-class transitions are undetected and fail open, so an expiration rule silently deletes live blobs and Glacier reads have no restore-and-retry path · DATA-LOSS — resolution: 📐 by-design / 🛠 docs (prev CAS-016 + prev CAS-017 — Filimonov: "lifecycle expiration must be off like versioning; add explicit user-facing bucket requirements; hard to detect without admin access"; "do not enable Object Lock/WORM/retention/lifecycle/versioning on the bucket — plain bucket only") · the Glacier restore-and-retry half stays open under prev CAS-052 — CA/Backend/CasObjectStorageBackend.cpp:272-282
  • CAS-013 One node admitting a hash algorithm rewrites the pool-wide reader floor to its own build number — mounting one disk with blob_hash_allow_new=1 locks every older build out of the entire pool · COMPAT — resolution: ↗ out-of-scope / deferred (prev CAS-009 — Filimonov: "needs attention later; not a blocker; model may be wrong") — CA/Pool/CasPoolMeta.cpp:72
  • CAS-014 The file-placement classifier is a closed suffix allowlist that misses shipped MergeTree file names (primary.cidx, .mrk4, secondary-index data files), sending them down the fully-in-memory inline path · CORRECTNESS — resolution: 🆕 new this round — CA/ContentAddressedTransaction.cpp:65-73
  • CAS-015 Waits on CAS single-flight, leader and recovery paths have no deadline and no cancellation, so a stalled store hangs concurrent INSERTs, readers of a recovering namespace and DROP TABLE · LIVENESS — resolution: 🔴 still-present (carried from prev CAS-034 + prev CAS-083) · evidence strengthened — CA/Pool/CasRefLedger.cpp:1457-1492
  • CAS-016 attempt_timeout_ms never reaches the wire and the blob payload read bypasses the CAS backend entirely, so reads run on the default 500-retry profile with no deadline and no classification · LIVENESS — resolution: 🆕 new this round — CA/Backend/CasRequestControl.h:84
  • CAS-017 Namespace removal latches read/write admission closed before anything is durable, and the ref lane has terminal states with no exit, so a transient backend error leaves the table permanently unusable · LIVENESS — resolution: 🆕 new this round — CA/Pool/CasRefLedger.cpp:3451-3458
  • CAS-018 Latches and leadership are set or released outside RAII and noexcept/destructor paths allocate, so a throw under a memory limit deadlocks the namespace forever or terminates the process · LIVENESS — resolution: 🆕 new this round — CA/Pool/CasRefLedger.cpp:1519-1541
  • CAS-019 Part-folder single flight is keyed by ref only, collapsing different manifest ids onto one key, so a follower straddling a repoint receives the leader's view for a different manifest than it resolved · CORRECTNESS — resolution: 🆕 new this round — CA/Parts/PartFolderAccess.cpp:231-269
  • CAS-020 getStorageObjects returns objects that are not the file's bytes because the envelope offset is dropped, so MOVE PART/PARTITION TO DISK and TTL moves produce a corrupt destination part with no error · INTEGRITY — resolution: 🔴 still-present (carried from prev CAS-047) · evidence strengthened — now with a concrete corrupting consumer — CA/ContentAddressedMetadataStorage.cpp:1336-1340
  • CAS-021 Ambiguous conditional-write outcomes are reported as definite ones: content equality is treated as proof of our own authorship, and a landed-then-timed-out write is reported as another writer's object · INTEGRITY — resolution: 🔴 still-present (carried from prev CAS-035) · evidence strengthened — now bidirectional — CA/Backend/CasRequestControl.cpp:427-435 CAS-021: Ambiguous conditional-write outcomes are reported as definite ownership #2207
  • CAS-022 The orphan-manifest sweep applies no protection at all to a manifest whose namespace has no catalog row — it deletes the body and emits BlobSourceRetirement for every entry during a namespace's first write · DATA-LOSS — resolution: 🆕 new this round — CA/Gc/CasOrphanManifestSweep.cpp:546
  • CAS-023 Deletes are accepted and silently do nothing when GC is disabled or the pool has settled as vanished — with gc_enabled=false every manual reclamation verb is refused with BAD_ARGUMENTS · DATA-LOSS — resolution: 🔴 still-present (carried from prev CAS-043) · evidence strengthened — partially inherited: the GC-deferred-reclaim-latency half is closed by prev CAS-018 (📐/❌ — Filimonov: "erase SLA is not part of the disk contract; operator can GC RUN anytime"); the gc_enabled=false accept-and-do-nothing and vanished-pool silent-success halves are open — CA/ContentAddressedMetadataStorage.cpp:611
  • CAS-024 Two CAS disks sharing a pool and a server_root_id resolve to one (namespace, ref), so a MOVE PARTITION TO DISK between them publishes then drops the same ref and the part is gone, silently · DATA-LOSS — resolution: 🔴 still-present (carried from prev CAS-064) · evidence strengthened — escalated to silent data loss — CA/ContentAddressedMetadataStorage.cpp:886-889

🟠 Medium

  • CAS-025 GC REBUILD discards the condemn universe and permanently orphans already-unreferenced blobs — prior_runs starts empty, the fold takes only +1 deltas, and the graduation guard is vacuous · LEAK — resolution: 🔴 still-present (carried from prev CAS-015) · anchor relocated — CA/Gc/CasGc.cpp:2809-2824
  • CAS-026 Relink treats pool_uuid equality as proof of "same bucket" and publishes the adopted part unverified — no presence check on any blob dependency and check_consistency=false on publish · INTEGRITY — resolution: 🔴 still-present (carried from prev CAS-031) · evidence strengthened — src/Storages/MergeTree/DataPartsExchange.cpp:313-330
  • CAS-027 Any bucket-credential peer can permanently disable, fence or misdirect another member; there is no intra-pool authentication — one PUT to a peer's owner/mount retires it or steals its slot · SECURITY — resolution: 📐 by-design (prev CAS-004 + prev CAS-211 — Filimonov: "bucket credential = whole trust boundary; all pool users same trust"; "bad actor with pool access is already doomed; provenance self-asserted / content-equality observable") — CA/Pool/CasServerRoot.cpp:68-79
  • CAS-028 Blob keys are unsalted pool-global content hashes: no per-subject shred, guessable residue, and a dedup confirmation oracle observable from system.cas_log · SECURITY — resolution: 📐 by-design / ❌ wontfix (prev CAS-019 + prev CAS-211 — Filimonov: "this is the essence of CAS dedup — will not "fix"") · the crypto-shred half stays open under prev CAS-071 — CA/Pool/CasPartWriteTxn.cpp:145
  • CAS-029 The provider dialect is declared by configuration and never detected, so the one bucket-versioning precondition runs only for GCS clients and fails open when it does run · DATA-LOSS — resolution: ⚠️ was-fixed / still-present (prev CAS-011 was ✅ "CAS checks at startup that versioning is off; versioned buckets unsupported") — the check is skipped entirely on AWS S3 and every S3-compatible store, and downgraded to a warning on GCS, so the residue is detection rather than support — CA/Backend/CasObjectStorageBackend.cpp:53-67
  • CAS-030 skip_access_check removes every bucket-configuration defense, is not recorded anywhere in the pool, and the decommission remount hard-codes it · CONFIG — resolution: 🆕 new this round — CA/Pool/CasPool.cpp:339-347
  • CAS-031 The conditional-write contract is validated only for single-PUT and then assumed for multipart, so on a store that ignores If-None-Match on CompleteMultipartUpload every large blob loses exclusivity · INTEGRITY — resolution: 🆕 new this round — CA/Backend/CasProbe.cpp:42
  • CAS-032 Zero cross-region / replicated-bucket awareness — nothing binds a pool identity to an endpoint or region, so failover onto a CRR destination is indistinguishable from the primary · INTEGRITY — resolution: 🔴 still-present (carried from prev CAS-051) · anchor relocated — CA/Pool/CasPoolMeta.cpp:100-104
  • CAS-033 All reclamation is gated on a whole-pool "clean pass" predicate with no bound and no retention signal — one anomaly anywhere suppresses every destructive action pool-wide indefinitely · LEAK — resolution: 📐 by-design (prev CAS-033 — Filimonov: "prefer fail-closed safety under GC uncertainty; reclaim may stall") — CA/Gc/CasGc.cpp:2063-2064
  • CAS-034 Per-round reclamation budgets sit below the steady-state creation rate — 5,000 ref objects per 60 s round and one 1,000-key janitor page, while every part commit creates two ref objects · LEAK — resolution: 📐 by-design / ❌ wontfix (prev CAS-018 + prev CAS-106 — Filimonov: "erase SLA is not part of the disk contract; operator can GC RUN anytime; GC cadence ~5–10 min; GDPR faster-than-that unlikely") · the budget-versus-rate arithmetic is not disposed of by that verdict and should be carried as an open question — CA/ContentAddressedSettings.cpp:46
  • CAS-035 The GC fold and its enumerations are O(total pool) every round, unbudgeted, with unbounded peak memory — the full ref LIST is retained with no cursor and is called even on deferred rounds · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-057 + prev CAS-050) · evidence strengthened — contradicts prev CAS-057's 🟡 "single LIST, no fan-out" mitigation — CA/Gc/CasBlobInDegree.cpp:484-555
  • CAS-036 Any bucket-sourced control object is materialized in memory unbounded and one planted object costs quadratic CPU — a multi-gigabyte _pool_meta or ref-log body OOMs the victim before any format check · DECODE/DoS — resolution: ⚠️ was-fixed / still-present (prev CAS-026 was ✅ "protobuf/binary formats removed → self-describing text") — removing protobuf removed the ParseFromArray specifics, but bucket-sourced bytes are still sized from attacker-declared values with no cap, at one of the two lines the fix cited — CA/Backend/CasObjectStorageBackend.cpp:284-293
  • CAS-037 Every CAS numeric field silently wraps mod 2^64 and offset + length overflows on the read path, so a planted manifest sz produces a wrapped read window that defeats every decoder range gate · DECODE/DoS — resolution: ⚠️ was-fixed / still-present (prev CAS-039 was ✅ "logical_size removed from envelope") — deleting the field removed the one documented exploit; the arithmetic itself was never hardened and the same wrap is now reachable on the read path — CA/Formats/CasTextFormat.cpp:193-223
  • CAS-038 Decoders make liveness- and safety-critical fields optional and default them to the least-safe value, so a truncated mount lease decodes as "expired, unfenced" and junk fold seals decode unvalidated · DECODE/DoS — resolution: 🔴 still-present (carried from prev CAS-077) · evidence strengthened — now systemic across four decoders — CA/Formats/CasServerRootFormats.cpp:147-169
  • CAS-039 gc_shards is adopted from bucket bytes with no upper bound and silently overrides the node's configured value while sizing vectors and loop bounds, on only a >= 1 check · DECODE/DoS — resolution: 🔴 still-present (carried from prev CAS-066) · evidence strengthened · anchor relocated — CA/Formats/CasPoolMetaFormat.cpp:116
  • CAS-040 The part-manifest payload-zone banner is written raw and validated only on decode, so an entry path containing LF encodes successfully and produces a committed part that can never be decoded again · INTEGRITY — resolution: 🆕 new this round — CA/Formats/CasPartManifestFormat.cpp:64-67
  • CAS-041 The manifest payload digest is recomputed by canonical re-encode of the decoded model, so any tolerated or foreign field reads as CORRUPTED_DATA — and the same implementation costs 2x work per decode · COMPAT — resolution: 🔴 still-present (carried from prev CAS-027) · evidence strengthened — same root cause, harder consequence: silent additive-field loss became a hard fail — CA/Formats/CasPartManifestFormat.cpp:263-267
  • CAS-042 One global build number is stamped as every object's minimum reader and the per-format change-point registry is populated but never consulted, so one generation bump invalidates every format · COMPAT — resolution: ↗ out-of-scope / deferred (prev CAS-009 — Filimonov: "needs attention later; not a blocker; model may be wrong") · the Roster throw stays open under prev CAS-076 — CA/Formats/CasFormat.cpp:82-93
  • CAS-043 The relink handshake negotiates a replication protocol number that says nothing about CAS generation, and the generation mismatch escapes the byte-fetch fallback, so a skewed fetch fails outright · COMPAT — resolution: ⚠️ was-fixed / still-present (prev CAS-209 was an adjudicated verified-safe 📐 "fail-closed publish-nothing → byte-fetch fallback; format bumps caught by the manifest's own compatibility check") — the fallback's catch filters CORRUPTED_DATA only while the generation check throws UNKNOWN_FORMAT_VERSION, so a skewed fetch fails outright instead of degrading — CA/ContentAddressedMetadataStorage.cpp:1610-1619
  • CAS-044 The 16 MiB per-part inline budget is enforced only at commit with no fallback, so a legitimately wide part fails its INSERT permanently and reproducibly with no re-classification to blob placement · FEATURE-GAP — resolution: 🔴 still-present (carried from prev CAS-100) · anchor relocated — CA/Pool/CasPartWriteTxn.cpp:54
  • CAS-045 The part-folder view cache accounts every retained manifest as 256 bytes because both producers hardwire .manifest_size = 0, so part_folder_cache_bytes is inoperative and nothing is excluded as oversized · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-049) · evidence strengthened — contradicts the 🟡 "bytes+count LRU" mitigation — CA/Parts/PartFolderAccess.cpp:128-131
  • CAS-046 Local scratch staging is unreserved, unaccounted, uncapped, held for the whole transaction and never swept at startup, while the CAS disk reports no free space at all · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-096) · evidence strengthened — CA/ContentAddressedTransaction.cpp:1223-1235
  • CAS-047 The blob upload pool is process-global, 16 threads with a 16-slot queue, and enqueue blocks — one wide part exceeds the queue by itself and all CAS uploads for every disk serialize behind it · PERF/SCALE — resolution: 🆕 new this round — CA/Pool/CasBlobUploadPool.cpp:45-49
  • CAS-048 A CAS part publish runs object-store I/O while DataPartsLock is held, so a throttling or 5xx bucket stalls the table's parts lock and blocks every SELECT, merge scheduling and part-set mutation · LIVENESS — resolution: ⚠️ was-fixed / still-present (prev CAS-006 was ✅ "durable publish no longer runs under DataPartsLock") — the off-lock publish exists on the replicated renameParts() path but was not applied to the covering-part path used by DROP/REPLACE PARTITION; the prior round said the same in the untriaged NEW-BC7-1src/Storages/MergeTree/MergeTreeData.cpp:5918-5922
  • CAS-049 GC STOP, shutdown and FSCK serialize behind whole in-flight unbounded scans, so an operator cannot stop GC, forget a disk or shut down while a round or scan is in flight against a slow bucket · LIVENESS — resolution: 🆕 new this round — CA/Gc/CasGcScheduler.cpp:213
  • CAS-050 The GC scheduler joins its thread objects outside the mutex that guards them and its threads self-exit independently, leaving a joinable-but-dead scheduler that reports itself as running · CONCURRENCY — resolution: 🆕 new this round — CA/Gc/CasGcScheduler.cpp:67-79
  • CAS-051 Snapshot-publish dispatch can leak its pending count, hanging quiesceRefTablesForRemount and dropNamespaceImpl forever, and its fan-out is unbounded pool-wide · LIVENESS — resolution: 🆕 new this round — CA/Pool/CasRefLedger.cpp:2754-2783
  • CAS-052 Anomaly reporting calls shared_from_this() on a possibly expiring pool, throwing bad_weak_ptr out of an unguarded region or handing a detached thread a pool being destroyed · CONCURRENCY — resolution: 🆕 new this round — CA/Pool/CasPool.cpp:972-1029
  • CAS-053 The ref-table runtime cache budget is enforced only at recovery, cannot evict a table being written, and its arithmetic can underflow and evict every evictable table at once · PERF/SCALE — resolution: 🆕 new this round — CA/Pool/CasRefLedger.cpp:1149-1210
  • CAS-054 Ref publication re-encodes the whole namespace every 256 transactions and obtains row byte counts by re-serializing rows; debug and sanitizer builds turn every txn apply into a further O(R) re-encode · PERF/SCALE — resolution: 🆕 new this round — CA/Pool/CasRefLedger.cpp:2741-2745
  • CAS-055 Every hardlinked file re-reads the source part manifest from the object store because part_folder_validate ships as always, costing hundreds to thousands of round trips per mutation · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-086) · evidence strengthened · anchor relocated — CA/ContentAddressedTransaction.cpp:816
  • CAS-056 A single-file write or unlink on a committed part republishes the whole manifest twice and emits one adopt event per blob entry inside the ref-log CAS lambda · PERF/SCALE — resolution: 🆕 new this round — CA/ContentAddressedTransaction.cpp:256-290
  • CAS-057 moveFile/replaceFile on a committed part file always throws LOGICAL_ERROR, so the standard "write .tmp then replaceFile" pattern is unusable and unique-key delete bitmaps fail on CAS · FEATURE-GAP — resolution: 🚫 not-a-bug / 🟡 soft (prev CAS-007 — Filimonov: "should be fine — tests catch nothing; glance someday — not a blocker") · the dismissal was explicitly soft and the fresh evidence names a real caller, so this should be re-opened — CA/ContentAddressedTransaction.cpp:1030-1055
  • CAS-058 Cross-disk ATTACH/REPLACE PARTITION FROM into a CAS disk is unimplemented and fails part-way through the part, leaving partial state rather than being rejected up front · FEATURE-GAP — resolution: 🆕 new this round — src/Storages/MergeTree/DataPartStorageOnDiskBase.cpp:593-621
  • CAS-059 An encrypted disk can be layered over CAS with no capability check and reports "not content-addressed" while still routing writes into CAS, disabling every CAS-aware MergeTree hook · CONFIG — resolution: ↗ out-of-scope (prev CAS-046 — Filimonov: "CAS+encryption needs design/dev/testing; should be workable later — not now") · the missing gate itself stays open under prev CAS-113 — src/Disks/DiskEncrypted.cpp:190-208
  • CAS-060 Per-file random IV makes every file a unique blob, silently destroying dedup — two replicas merging identical rows produce entirely distinct blobs and nothing detects it · CORRECTNESS — resolution: ↗ out-of-scope (prev CAS-046 — Filimonov: "CAS+encryption needs design/dev/testing; should be workable later — not now") — src/Disks/DiskEncryptedTransaction.cpp:105-112
  • CAS-061 Only gc/state has a rebuild path; every other control object has no DR path, and because the tools open the pool through _pool_meta first, damage to that one object disables the DR tools themselves · OBSERV/DAY2 — resolution: 🔴 still-present (carried from prev CAS-063) · evidence strengthened — CA/Pool/CasPool.cpp:293-368
  • CAS-062 SYSTEM CAS FSCK is counts-only and no repair path exists anywhere — the SQL path can say the pool is corrupt but never which keys, with no timeout and no scoping · OBSERV/DAY2 — resolution: ⚠️ was-fixed / still-present (prev CAS-013 was ✅ "SQL fsck landed; still slow vs GC — backlog to speed up") — making the diagnostic callable without making it informative does not deliver prev CAS-013's premise; the repair half remains separately open as prev CAS-093 — src/Interpreters/InterpreterSystemQuery.cpp:2534
  • CAS-063 The only way to clear a dead member's mount slot is a verb that first erases that member's data, and a half-decommissioned member is invisible to cas_mounts and not repairable by a re-run · OBSERV/DAY2 — resolution: 🔴 still-present (carried from prev CAS-062, which was 🛠 will-fix) · anchor relocated — CA/Tools/CasDecommission.cpp:137-183
  • CAS-064 No CAS decoder is fuzzed, no property-based tests exist, and three live format classes skip the shared failure-mode battery, although every decoder consumes bucket-sourced input · TEST-GAP — resolution: 📐 by-design / YAGNI (prev CAS-010 — Filimonov: "same trust model as CAS-005 — trust S3; less trust ⇒ more perf loss; no decoder fuzz mandate as gate") — src/Disks/tests/cas_format_test_battery.h
  • CAS-065 No CI lane exercises a native or GCS conditional-write dialect — the exclusivity guarantee CAS is built on is only ever tested against the emulated in-process backend · TEST-GAP — resolution: ⚠️ was-fixed / still-present (prev CAS-012 was ✅ "e2e tested on real S3 and GCS; Azure still not") — a static audit cannot see a manual run, but what stands is that the most safety-critical path has no repeatable regression-protecting lane — src/Disks/tests/gtest_cas_backend_contract.cpp:250-258
  • CAS-066 Emulated single-process mode is chosen by storage type alone with no override and only an INFO-level warning, so two servers over one shared local path both get in-process-only exclusivity · CONFIG — resolution: 🔴 still-present (carried from prev CAS-065) · evidence strengthened — the read-only-mount gap and the emulated-mode auto-selection are new sub-findings — CA/ContentAddressedMetadataStorage.cpp:509-520
  • CAS-067 The emulated conditional-write token is a filesystem mtime, so coarse mtime granularity validates a stale token, and its state pruning stalls permanently on clock skew · INTEGRITY — resolution: 🆕 new this round — CA/Backend/CasObjectStorageBackend.cpp:347-362
  • CAS-068 putIfAbsentControlled swallows deterministic local failures and reports them as ambiguity, so a LOGICAL_ERROR or NOT_IMPLEMENTED that provably never landed wedges the ref lane into recovery · INTEGRITY — resolution: 🆕 new this round — CA/Backend/CasRequestControl.cpp:271-281
  • CAS-069 Empty catches reclassify transient read failures as corruption — a MEMORY_LIMIT_EXCEEDED on a large gc/state is indistinguishable from genuine corruption and drives a full baseline rebuild · INTEGRITY — resolution: 🆕 new this round — CA/Gc/CasGc.cpp:2633-2648
  • CAS-070 Remount self-healing is permanently disabled by a lost wakeup, a latched flag, or one unhandled throw — after a fence-out the mount stays fenced closed until process restart · LIVENESS — resolution: 🆕 new this round — CA/Pool/CasMountRuntime.cpp:341-369
  • CAS-071 Mount and pool state is read and written outside the mutex that guards it — keeper replacement races readers, fence/deadline state is torn, and pool identity is published under a TSA suppression · CONCURRENCY — resolution: 📐 by-design (prev CAS-090, whose verdict is recorded with anchors: Pool/CasMountRuntime.h:400; renewWatermarkOnce unlocked at :156-163; reassign under Pool::remount_mutex only) — CA/Pool/CasMountRuntime.h:164
  • CAS-072 Staged-manifest debris cleanup tracks only one precommit binding and can delete a body a live precommit still owns, leaving a durable reference to a missing object · DATA-LOSS — resolution: 🆕 new this round — CA/Pool/CasPartWriteTxn.cpp:866-884
  • CAS-073 The condemn marker is not incarnation-scoped, is accepted as its own proof, and is never cleared, so a marker written for a previous incarnation licenses deleting the new one · INTEGRITY — resolution: 🆕 new this round — CA/Formats/CasBlobMetaFormat.h:14-22
  • CAS-074 Generation prune advances a monotone cursor past still-referenced generations and the compensating hand-off is one-shot, so nothing ever revisits snap_pruned_through and those objects leak · LEAK — resolution: 🔴 still-present (carried from prev CAS-072) · evidence strengthened — CA/Gc/CasGc.cpp:2456-2500
  • CAS-075 The blob body is durable before its meta marker and no GC phase enumerates bodies, so a crash in the window leaves a body reclaimed by no sweep at all and excluded from the clean() verdict · LEAK — resolution: 🔴 still-present (carried from prev CAS-060) · evidence strengthened — CA/Pool/CasPartWriteTxn.cpp:423-429
  • CAS-076 GC seals a generation before committing gc/state, so repeated crashes accumulate complete-looking seals for generations no state references and nothing prunes them · LEAK — resolution: 🔴 still-present (carried from prev CAS-072 + prev CAS-108) · anchor relocated — CA/Gc/CasGc.cpp:2254
  • CAS-077 A permanently lost node pins its own manifest debris as unreclaimable — the sweep needs the owning mount lease for a watermark floor, and the only verb that removes the lease also erases its data · LEAK — resolution: 🆕 new this round — CA/Gc/CasOrphanManifestSweep.cpp:373-387
  • CAS-078 The namespace janitor rewinds its durable cursor to the beginning on a transient LIST failure, so one 5xx discards all prior progress on a janitor already limited to one page per round · LEAK — resolution: 🆕 new this round — CA/Gc/CasNamespaceJanitor.cpp:22-31
  • CAS-079 Ref-object trimming is starved by any concurrent catalog mutation anywhere in the pool — one shared catalog object means any CREATE/DROP aborts all ref-log and snapshot trimming for the round · LEAK — resolution: 🆕 new this round — CA/Gc/CasGc.cpp:2320-2336
  • CAS-080 A snapshot published without a checkpoint advance is not re-driven on a quiescent namespace, so the namespace pays full log replay on every recovery until the table is written again · CORRECTNESS — resolution: 🆕 new this round — CA/Pool/CasRefLedger.cpp:2937-3035
  • CAS-081 S3 staging residue is retained on abort and swept only for one's own server_root_id, so whole part-file plaintext persists after any killed INSERT and no shipped tool ever lists it · LEAK — resolution: 🔴 still-present (carried from prev CAS-060) · evidence strengthened — CA/ContentAddressedTransaction.cpp:148-172
  • CAS-082 There is no multipart-upload hygiene anywhere in CAS and capability-probe debris is explicitly excluded from the residual scan — incomplete uploads are billed, invisible to fsck and never aborted · LEAK — resolution: 🔴 still-present (carried from prev CAS-084) · evidence strengthened — contradicts the dismissal's premise ("MPU leftovers typical for S3, GC cleans eventually"): nothing in CAS ever cleans them — CA/Backend/CasProbe.cpp:20-32
  • CAS-083 Lightweight DELETE and mutations free the deleted rows' bytes only for rewritten files — surviving entries are republished against the same blobs, so deleted row bytes stay in the pool · LEAK — resolution: 📐 by-design / ❌ wontfix (prev CAS-019 + prev CAS-018 — Filimonov: "this is the essence of CAS dedup — will not "fix"") — CA/ContentAddressedTransaction.cpp:782-829
  • CAS-084 Reclaimed blobs are never evicted from the node-local filesystem cache, so after GC deletes a blob its bytes stay on local disk: capacity is held and deleted content is still readable · LEAK — resolution: 🆕 new this round — src/Disks/DiskObjectStorage/DiskObjectStorageCache.cpp:21-23
  • CAS-085 always_use_copy_instead_of_hardlinks=1 makes every CAS clone and mutation throw NOT_IMPLEMENTED and nothing rejects the setting, breaking mutations, FREEZE and ATTACH/REPLACE PARTITION · FEATURE-GAP — resolution: 🔴 still-present (carried from prev CAS-112) · evidence strengthened — no longer latent: real MergeTree callers reach it — CA/ContentAddressedTransaction.cpp:363-366
  • CAS-086 IDisk directory and metadata queries deviate from the contract their generic callers rely on, so removeDirectory's non-empty guard never fires and a populated part directory is dropped without error · CORRECTNESS — resolution: 🔴 still-present (carried from prev CAS-048 + prev CAS-099) · evidence strengthened — now a contract-level cluster — CA/ContentAddressedMetadataStorage.cpp:1293-1305
  • CAS-087 The part-path parser lets a component named detached/moving outrank part-dir detection, and silently reinterprets unclassified part dirs as table-level files, so misclassification surfaces only on read · CORRECTNESS — resolution: 🔴 still-present (carried from prev CAS-073) · evidence strengthened — same file, now both directions — CA/Parts/PartPathParser.cpp:140-162
  • CAS-088 resurrect is an unconditional, budget-free, fence-unchecked overwrite that returns a token it did not write, so two writers on one condemned blob can each believe they own the object · INTEGRITY — resolution: 🆕 new this round — CA/Backend/CasObjectStorageBackend.cpp:814-856
  • CAS-089 The blob envelope is written but never read back: the offset comes from pool meta and its identity field is silently truncatable, so any divergence in envelope length goes undetected · INTEGRITY — resolution: 🔴 still-present (carried from prev CAS-024) · evidence strengthened — CA/Pool/CasManifestReader.cpp:137-144
  • CAS-090 Encryption interactions: SSE-C breaks staging promotion, CAS manifest metadata stays plaintext, AES-CTR carries no MAC, and immutable shared blobs make re-keying a pool impossible · SECURITY — resolution: 🔴 still-present (carried from prev CAS-113) · evidence strengthened — prev CAS-204's 📐 (SSE-S3/KMS transparent) is unaffected; SSE-C is the case that breaks — src/IO/S3/Client.cpp:1273-1287
  • CAS-091 Layout::checkNamespace accepts . and .. segments unlike every other CAS path validator, so under the auto-selected emulated mode over local object storage the traversal is real on the filesystem · SECURITY — resolution: 🔴 still-present (carried from prev CAS-074) · evidence strengthened — traversal is now real rather than theoretical — CA/Formats/CasLayout.cpp:295-319
  • CAS-092 The write fence and the request it admits are on different clocks, and one cross-node wall-clock liveness gate survives on the decommission epoch mint; both CLOCK_BOOTTIME reads are Linux-only · CORRECTNESS — resolution: ⚠️ was-fixed / still-present (prev CAS-030 was ✅ "lease liveness via token stability on observer clock, no cross-node wall-clock trust") — the fix removed cross-node wall-clock trust from lease renewal; one such decision remains on the destructive SYSTEM CAS DROP MEMBER path — CA/Pool/CasServerRoot.cpp:196-209
  • CAS-093 The temp text-index directory lives inside the part, publishes a ref before the part exists, and its removeRecursive cleanup is one of the silent no-ops, so the temp ref and its blobs survive · CORRECTNESS — resolution: 🔴 still-present (carried from prev CAS-061) · evidence strengthened — promoted from test-gap to defect — src/Storages/MergeTree/TextIndexUtils.cpp:601-609
  • CAS-094 GC REBUILD refusals and failures are not side-effect free — a refused rebuild reports performed=0 while leaving complete-looking run objects and a fold seal a later round can adopt or reject · INTEGRITY — resolution: 🔴 still-present (carried from prev CAS-108) · anchor relocated — CA/Gc/CasGc.cpp:2811-2824
  • CAS-095 cas-gc-dryrun is not a preview of the next round and is silently empty in exactly the disaster state it exists for — a damaged gc/state yields preview_deletes=0, as if nothing needed deleting · OBSERV/DAY2 — resolution: 🆕 new this round — CA/Gc/CasGc.cpp:3017-3080
  • CAS-096 The rebuild reports almost nothing about the quality of the baseline it blesses — a rebuild that silently drops a parent row and its hold reports performed=1 and nothing else · OBSERV/DAY2 — resolution: 🆕 new this round — CA/Gc/CasGc.h:48-63
  • CAS-097 No surface names namespaces or raw keys, and cas-inspect cannot decode 8 of the 18 CAS formats, mis-decodes _files/ names and requires a raw key no shipped command can enumerate · OBSERV/DAY2 — resolution: 🔴 still-present (carried from prev CAS-062, which was 🛠 will-fix) · anchor relocated — CA/Tools/CasInspect.cpp:517-576
  • CAS-098 GC health is process-local and ephemeral, GC STOP is node-local and unobservable, and last_success_age_seconds=0 means both "never led" and "succeeded just now" · OBSERV/DAY2 — resolution: ⚠️ was-fixed / still-present (prev CAS-014 was ✅/🛠 "should be OK now; worth re-verify — not a blocker") — the metric surface landed but the specific silent failure it was added for is still invisible; this is that re-verify and it fails — CA/Gc/CasGcScheduler.cpp:312-327
  • CAS-099 Rolling restart and planned node removal have no quiesce, drain or leadership-handoff verb — the only shutdown is "stop and join", so a rolling restart drops GC leadership abruptly · OBSERV/DAY2 — resolution: 🆕 new this round — CA/Gc/CasGcScheduler.cpp:67-79
  • CAS-100 fsck skips whole check families yet still reports a clean, non-partial result — source-edge checksums are never verified on a healthy pool and the stale-edge finding is unreachable from SQL · OBSERV/DAY2 — resolution: 🔴 still-present (carried from prev CAS-093) · evidence strengthened — the verdict is unsound, not merely unrepaired — CA/Tools/CasFsck.cpp:654
  • CAS-101 GC round counters are derived from budget-truncated logs and phase observability reports constants and the wrong round, so after a large DROP the round undercounts real deletes · OBSERV/DAY2 — resolution: 🆕 new this round — CA/Gc/CasGc.cpp:652-690
  • CAS-102 11 of the 156 CAS ProfileEvents can never fire and server-root I/O is counted as GC I/O, so lease and mount request volume cannot be measured or alerted on · OBSERV/DAY2 — resolution: ⚪ info (prev CAS-214 — recorded as "classifyCasNs uses unanchored substring match (metric misattribution only, no correctness impact)") — CA/Backend/CasInstrumentedBackend.cpp:109-122
  • CAS-103 Savings and outcome counters are incremented before the outcome they claim is decided, so dedup-savings metrics count bytes that were in fact uploaded, over-reporting the headline value of the feature · OBSERV/DAY2 — resolution: 🆕 new this round — CA/Pool/CasPartWriteTxn.cpp:155-175
  • CAS-104 Audit-event dispatch funnels read and write hot paths through one mutex and the shipped config enables the sink by default, so every ref resolve and manifest read serializes on that mutex · PERF/SCALE — resolution: 🆕 new this round — CA/Pool/CasEventDispatcher.cpp:17-44
  • CAS-105 The whole mount/lease/request budget and seven pool-level caps are unreachable from configuration, so none of the scaling problems in this report can be tuned and the validator that exists is dead code · CONFIG — resolution: 🆕 new this round — CA/ContentAddressedSettings.cpp:29-58
  • CAS-106 The non-CAS config key allowlist is a fixed 18-entry set, so ordinary object-storage keys (connect_timeout_ms, max_connections, SSE keys) abort disk registration rather than being passed through · CONFIG — resolution: 🆕 new this round — CA/ContentAddressedSettings.cpp:23-27
  • CAS-107 No CAS setting can be changed by config reload, the ignore is silent, and a removed CAS disk keeps its mount lease held so the slot cannot be reclaimed by anyone else · CONFIG — resolution: 🆕 new this round — src/Disks/DiskObjectStorage/MetadataStorages/IMetadataStorage.h:340-343
  • CAS-108 Dead code and test-only seams are compiled into the production binary — fault-injection surfaces link into clickhouse-server and a process-global unsynchronized test hook is read racily on every part commit · TEST-GAP — resolution: 🆕 new this round — CA/Backend/CasInMemoryBackend.{h,cpp}
  • CAS-109 There is no deterministic crash-at-step-N harness and settings validation has one fail-closed test for the whole surface, so every crash-consistency window in this round is uncovered · TEST-GAP — resolution: 🆕 new this round — src/Disks/tests/gtest_cas_settings.cpp
  • CAS-110 resolveRef's allow_stale is plumbed through two layers and silently discarded, so callers that explicitly ask for a stale-tolerant resolve get the strict path and can block on recovery or throw · CORRECTNESS — resolution: ⚠️ was-fixed / still-present (prev CAS-085 was ✅ "allow_stale retired in code") — "retired" was implemented as "ignored" rather than "removed", so the callers that relied on the semantics lost them with no compile error — CA/Pool/CasRefLedger.cpp:214-215
  • CAS-111 The per-namespace 64 MiB ref-table admission cap fails writes permanently and non-retryably at roughly 610k refs, reached by ordinary part accumulation on a wide-partitioned table · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-100) · evidence strengthened — quantified; also qualifies prev CAS-008's ✅, since write availability is still coupled to a 64 MiB encoded-object ceiling on live state where trimming cannot help — CA/Pool/CasRefLedger.cpp:2161-2169
  • CAS-112 Every ref append re-reads and linearly rescans the pool-global ref catalog — at least two full catalog GETs plus linear scans per part commit, scaling in the number of namespaces in the pool · PERF/SCALE — resolution: 🆕 new this round — CA/Pool/CasRefCatalog.cpp:109
  • CAS-113 Encoded-size caps are validated only after the oversized buffer has been built, so hitting a 256 MiB object cap requires first materializing 256 MiB — a memory spike rather than an early rejection · PERF/SCALE — resolution: 🆕 new this round — CA/Pool/CasPartWriteTxn.cpp:541-544
  • CAS-114 Recovery must seal every skipped writer epoch one at a time, so first touch of an idle table costs O(mount generations) sequential durable write pairs with no cap · PERF/SCALE — resolution: 🆕 new this round — CA/Pool/CasRefLedger.cpp:740-824
  • CAS-115 Cache weight functions under-account their entries — PartManifestWeight misses ~2x of the per-entry footprint and DedupWeight returns a constant 64 (~3.1x under), so both byte budgets over-admit · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-049) · evidence strengthened — contradicts the 🟡 mitigation quantitatively — CA/Pool/CasManifestReader.h:49-58
  • CAS-116 Staging is quadratic in the number of files in a part — every staging mutation rescans all prior staged entries and moveDirectory re-scans the destination set once per source entry · PERF/SCALE — resolution: 🆕 new this round — CA/ContentAddressedTransaction.cpp:510
  • CAS-117 CAS stores one object plus one meta object plus a 256-byte envelope per part file, so a wide part of small files is dominated by per-object overhead in request count and stored bytes · PERF/SCALE — resolution: 🆕 new this round — CA/Pool/CasManifestReader.cpp:133-144
  • CAS-118 No cache can serve a read without a network round trip and one logical read resolves the ref several times — head() runs before the cache probe and resolve() before the view-cache probe · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-086 + prev CAS-067) · evidence strengthened — CA/Pool/CasManifestReader.cpp:56-78
  • CAS-119 Throttling amplification: conditional writes use a single-attempt client, so retry moves from the jittered SDK to CAS's un-jittered fixed backoff up to 16 times, each adding a resolution GET · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-053) · evidence strengthened — CA/Backend/CasObjectStorageBackend.cpp:628-639
  • CAS-120 There is no relink fast path for local moves inside one CAS pool and the CAS copy is serial — every CAS destination goes through a plain recursive readFile/writeFile loop, even CAS-to-CAS on one pool · PERF/SCALE — resolution: 🔴 still-present (carried from prev CAS-041) · evidence strengthened — independently confirmed twice — src/Storages/MergeTree/DataPartStorageOnDiskBase.cpp:702-718
  • CAS-121 A CAS table outside an Atomic database cannot be backed up at all, and every CAS backup re-reads every byte because areBlobPathsRandom() returns false and forces checksums FromReading · FEATURE-GAP — resolution: 🔴 still-present (carried from prev CAS-042) · evidence strengthened — src/Storages/MergeTree/DataPartStorageOnDiskBase.cpp:417-422
  • CAS-122 The !-prefixed critical-key escape hatch has no producer and strict formats report an additive field as data corruption, routing an operator to fsck/rebuild instead of a rollback · COMPAT — resolution: ⚠️ was-fixed / still-present (prev CAS-075 was ✅ "header_hash removed; !-key gate CasTextFormat.cpp:249–251") — the removal half landed, but the enforcement half it was traded against is dead code, so "critical extension" enforcement still relies on writer honesty — CA/Formats/CasTextFormat.cpp:240-242

🟡 Low / hardening

  • CAS-123 There is no byte accounting outside blobs/ and no reclaim-forecast surface, so a bucket-versus-table gap cannot be attributed to manifests, ref logs, snapshots, staging or generations · OBSERV/DAY2 — resolution: 🔴 still-present (carried from prev CAS-040) · anchor relocated — CA/Tools/CasFsck.cpp:578-596
  • CAS-124 Empty content hashes to the all-zero digest, which is also the sentinel fsck substitutes for an unparsable key, so a legitimate zero-length blob and a parse failure are indistinguishable · INTEGRITY — resolution: 🆕 new this round — CA/Primitives/CasBlobDigest.h:41
  • CAS-125 Xxh3Streamer dereferences a null state in its constructor, so the allocation-failure guard is dead and blob_hash=xxh3-128 under memory pressure faults instead of raising CANNOT_ALLOCATE_MEMORY · CORRECTNESS — resolution: 🆕 new this round — CA/Primitives/CasXxh3Streamer.h:17
  • CAS-126 The write-fence pre-check exists only on the S3 staging path, so on the default local backend a writer whose fence was lost mid-write streams the whole body and discovers the loss only at commit · INTEGRITY — resolution: 🆕 new this round — CA/ContentAddressedTransaction.cpp:607-622
  • CAS-127 Avoidable per-byte and per-line copying and allocation on hot paths — three full copies per byte on blob writes, byte-at-a-time readLine with no reserve, and repeated substring searches per request · PERF/SCALE — resolution: 🆕 new this round — CA/ContentAddressedTransaction.cpp:1220-1235
  • CAS-128 Inline entries staged into a destination part that never gets a build fail the whole commit closed — writing a small metadata file into part A then moving it into an unwritten part aborts the transaction · CORRECTNESS — resolution: 🆕 new this round — CA/ContentAddressedTransaction.cpp:800-812
  • CAS-129 The writer-epoch fence on a build is checked only at entry to promote, not at the durable append, so a build admitted under epoch E1 can append on a fresh runtime after a self-remount to E2 · CONCURRENCY — resolution: 🔴 still-present (carried from prev CAS-002) · evidence strengthened — this is the recheck Filimonov asked for ("looks overstated / "высосана из пальца"; maybe recheck carefully — not a blocker"); the window is bounded by operation_deadline_ms (90 s) and graded Low, consistent with "not a blocker" — CA/Pool/CasPartWriteTxn.cpp:125-128
  • CAS-130 Mount-lease and epoch identity fields are written but not enforced — doStart writes a literal seq = 1 over the slot claimMount just bumped, and allocateWriterEpoch can return 0 · CORRECTNESS — resolution: 🔴 still-present (carried from prev CAS-080 + prev CAS-029) · evidence strengthened — CA/Pool/CasServerRoot.cpp:1021
  • CAS-131 Audit-event and cache-counter attribution defects: timestamps and thread_id/query_id are taken on the draining thread, and cache-hit resolves are missing from the log · OBSERV/DAY2 — resolution: 🆕 new this round — CA/ContentAddressedMetadataStorage.cpp:431-456
  • CAS-132 Bucket layout, hostnames, PIDs and server UUIDs are disclosed in errors reachable by unprivileged SQL users and via system.cas_mounts, which is readable with SELECT on system · SECURITY — resolution: 🆕 new this round — CA/Pool/CasServerRoot.cpp:368-386
  • CAS-133 cas_mounts renders a transient LIST failure identically to a non-existent pool or a not-yet-started disk, so one throttled LIST makes a healthy pool look absent · OBSERV/DAY2 — resolution: 🆕 new this round — src/Storages/System/StorageSystemContentAddressedMounts.cpp:146-156
  • CAS-134 A receiver with two CAS pools in one policy advertises only the first, so fetches into the second pool silently transfer every byte instead of relinking, with no diagnostic · FEATURE-GAP — resolution: 🆕 new this round — src/Storages/MergeTree/DataPartsExchange.cpp:586-604
  • CAS-135 Emulated mode holds one mutex across multiple round trips per operation and a process-wide mutex across a whole blob body, serializing all CAS-over-local reads, writes and resurrects · PERF/SCALE — resolution: 🆕 new this round — CA/Backend/CasObjectStorageBackend.cpp:491-507

Genuine data-loss / correctness paths (the short list to look at first)

CAS-001 (pool-global shadow namespaces), CAS-002 (adopt bypasses the condemn marker), CAS-005
(unrevertible repoint), CAS-006 (rename ends in an unconditional source drop), CAS-022 (orphan sweep
with no catalog row), CAS-023 and CAS-024 (deletes that silently do nothing; a MOVE that erases the
part), plus the fencing pair CAS-003/CAS-010. Everything else biases to a reclaimable leak, a
liveness/operability cliff, or an unverified edge.

Separately, the highest-information group is the ⚠️ was-fixed / still-present set — CAS-029, CAS-036,
CAS-037, CAS-043, CAS-048, CAS-062, CAS-065, CAS-092, CAS-098, CAS-110, CAS-122 — because
each contradicts a verdict already recorded as fixed, so the current mental model is wrong there.
CAS-048, CAS-092, CAS-110, CAS-036, CAS-122, CAS-029 and CAS-098 are the safety- or
availability-relevant ones and should be read first.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions