Repository navigation
Conversation
…next commit) --- Original cherry-pick message follows: Merge pull request #2159 from Altinity/feature/antalya-26.6/CAS [experimental] CAS (Content-addressed storage) over shared object storage for antalya-26.6 # Conflicts: # .github/workflows/fast_builds.yml # .github/workflows/master.yml # .github/workflows/pull_request.yml # .github/workflows/pull_request_community.yml # ci/jobs/functional_tests.py # ci/jobs/scripts/clickhouse_proc.py # ci/jobs/scripts/clickhouse_service.py # ci/workflows/pull_request.py # ci/workflows/pull_request_community.py # ci/workflows/release_branches.py # docs/concepts/features/configuration/server-config/storing-data.mdx # programs/disks/DisksApp.cpp # programs/disks/ICommand.h # programs/server/Server.cpp # src/Common/FailPoint.cpp # src/Common/ThreadStatus.h # src/Core/ServerSettings.cpp # src/Disks/DiskObjectStorage/DiskObjectStorage.cpp # src/Disks/DiskObjectStorage/DiskObjectStorage.h # src/Disks/DiskObjectStorage/DiskObjectStorageTransaction.cpp # src/Disks/DiskObjectStorage/MetadataStorages/IMetadataStorage.h # src/Disks/DiskObjectStorage/MetadataStorages/MetadataStorageFactory.cpp # src/Disks/DiskObjectStorage/ObjectStorages/IObjectStorage.h # src/Disks/DiskObjectStorage/ObjectStorages/Local/LocalObjectStorage.cpp # src/Disks/DiskObjectStorage/ObjectStorages/S3/S3ObjectStorage.cpp # src/Disks/DiskObjectStorage/RegisterDiskObjectStorage.cpp # src/Disks/IDiskTransaction.h # src/IO/ReadPipeline.cpp # src/IO/ReadPipeline.h # src/IO/S3/copyS3File.cpp # src/IO/S3/copyS3File.h # src/IO/S3Common.cpp # src/IO/WriteBufferFromS3.cpp # src/IO/tests/gtest_writebuffer_s3.cpp # src/Interpreters/InterpreterSystemQuery.cpp # src/Interpreters/ServerAsynchronousMetrics.cpp # src/Parsers/ASTSystemQuery.h # src/Parsers/tests/gtest_Parser.cpp # src/Storages/MergeTree/DataPartStorageOnDiskBase.cpp # src/Storages/MergeTree/DataPartStorageOnDiskFull.cpp # src/Storages/MergeTree/DataPartsExchange.cpp # src/Storages/MergeTree/IMergeTreeDataPart.cpp # src/Storages/MergeTree/MergeTask.cpp # src/Storages/MergeTree/MergeTreeData.cpp # tests/integration/test_replicated_database/test.py # tests/queries/0_stateless/02253_empty_part_checksums.sh # tests/queries/0_stateless/02254_projection_broken_part.sh # tests/queries/0_stateless/02255_broken_parts_chain_on_start.sh # tests/queries/0_stateless/02369_lost_part_intersecting_merges.sh # tests/queries/0_stateless/02370_lost_part_intersecting_merges.sh # tests/queries/0_stateless/02444_async_broken_outdated_part_loading.sh # tests/queries/0_stateless/04215_replicated_missing_covered_part_on_start.sh # tests/queries/0_stateless/04327_reader_executor_metrics.sql # tests/queries/0_stateless/04328_reader_executor_kpi_async_metric.sql
Source PR: #2159 - `.github/workflows/*.yml` are generated: kept at the base version; the "Check Workflows" job regenerates them from `ci/workflows/*.py` (`python3 -m praktika yaml`), which now include the CAS lanes. - Stateless tests 02253/02254/02255/02369/02370/02444/04215 stay deleted: `antalya-26.8` converted them to integration tests (7f7a30e). - `ci/defs/altinity_jobs.py`: dropped the duplicated `cas_functional_tests_jobs` (already on base from 5aaf212). Adapted: DiskObjectStorageTransaction - PR's `dispatch` mapped onto base `addOperation` (made eager also for `transactionIsStagingOverlay`), keeps `appliesOperationsEagerly` for in-memory metadata Adapted: RegisterDiskObjectStorage - `use_fake_transaction` selection dropped, fake transactions were removed on base (c6254c3) Adapted: copyS3File - `copy_mode` threaded through base `copyS3FileImpl`; ranged copies use `ObjectStorageCopyMode::Default` Adapted: DataPartStorageOnDiskFull - in-flight reads moved into the base `*Impl` overrides (packed skip-index overlay lives in the base wrapper) Adapted: MergeTask/IMergeTreeDataPart - `use_parent_transaction` passed to base `getProjectionNoInitialize` Adapted: DataPartsExchange - kept base `REPLICATION_PROTOCOL_VERSION_WITH_INVALIDATED_SYSTEM_COLUMNS = 10`, negotiation tops out at `WITH_CA_CONFIRM = 11` Adapted: MergeTreeData - empty covering part already commits its disk transaction on base, PR's CAS-only commit dropped as redundant Adapted: functional_tests.py - RustFS start folded into base SeaweedFS-based `start` Source-PR: #2159 (#2159)
…next commit) --- Original cherry-pick message follows: Merge pull request #2300 from Altinity/feature/antalya-26.6/CAS-improvements CAS improvements # Conflicts: # src/Common/ErrorCodes.cpp # src/Disks/DiskObjectStorage/ObjectStorages/S3/S3ObjectStorage.cpp # src/Disks/DiskObjectStorage/ObjectStorages/S3/S3ObjectStorage.h # src/IO/ReadBufferFromS3.cpp # src/IO/ReadSettings.h # src/IO/S3/tests/TestPocoHTTPServer.h # src/IO/WriteSettings.h
- `ErrorCodes.cpp`: kept antalya-26.8 numbering (1009-1018) and the `CLICKHOUSE_PARSER_MINIMAL_BUILD` guard; added the PR's `CAS_WRITE_UNATTRIBUTED` (1037) and `CAS_DELETE_MARKER` (1038), END = 1038. Dropped the PR's comments describing the antalya-26.6 1009-1011 layout, which does not exist on antalya-26.8. - `S3ObjectStorage`: combined the PR's `client_slot`/`client` reference with antalya-26.8's `client_restricts_server_credentials`, the iterator's `description` with the PR's `attempt_seed`, and antalya-26.8's `UnknownSize`-aware size and `object.etag` args with the PR's `refresh_callback`. - `TestPocoHTTPServer.h`: kept `makeMockServerParams` and added the PR's dedicated thread pool and destructor. - Adapted the PR's `S3::setClickhouseAttemptNumber` calls to `S3::setClickHouseAttemptNumber`, renamed on antalya-26.8 by e8e1e12. Adapted: setClickhouseAttemptNumber -> setClickHouseAttemptNumber (renamed by e8e1e12 on antalya-26.8) Source-PR: #2300 (#2300)
…next commit) --- Original cherry-pick message follows: Merge PR #2307 (cas/configurable-mount-lease-timing) into feature/antalya-26.6/CAS-improvements-cicd-fixes Brings k-morozov's configurable CAS mount lease TTL / renewal period (#2307) so the heartbeat-model work can land on top of it while keeping the author's commits intact. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GhVd7eMAWdFubNk4g1B2Tx # Conflicts: # src/Disks/DiskObjectStorage/MetadataStorages/ContentAddressed/ContentAddressedMetadataStorage.cpp Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> # Conflicts: # docs/en/antalya/cas/configuration.md # src/Disks/DiskObjectStorage/MetadataStorages/ContentAddressed/ContentAddressedMetadataStorage.cpp # src/Disks/DiskObjectStorage/MetadataStorages/ContentAddressed/ContentAddressedMetadataStorage.h # src/Disks/DiskObjectStorage/MetadataStorages/ContentAddressed/ContentAddressedSettings.cpp
All changes from #2307 (`mount_lease_ttl_ms` / `mount_renew_period_ms` settings, members, wiring into `PoolConfig`, validation, docs) are already present on the base. The conflicting hunks keep the base side, which has later additions on top of them (`unsafe_remount_no_delay`, the `attempt_timeout_ms` check, `connect_timeout_cap_ms`, and the refined `cas_mount_renew_period_ms` doc text). Source-PR: #2307 (#2307)
…next commit) --- Original cherry-pick message follows: Merge pull request #2349 from Altinity/cas/memworker-split-allocated-source MemoryWorker: use sanitizer info for allocated counter # Conflicts: # src/Common/MemoryWorker.cpp
…next commit) --- Original cherry-pick message follows: Merge pull request #2385 from Altinity/fix/antalya-26.6/lldb-budget-and-sigcont CI: lldb stack budget by build flavor, and keep lldb's JIT breakpoint out of the server # Conflicts: # ci/tests/test_print_stacktraces.py # tests/clickhouse-test
tests/clickhouse-test: applied the PR's lldb budget and JIT-loader flag on top of the antalya-26.8 shape of get_stacktraces_from_lldb (sudo/shutil.which lldb path and keep_output_on_error kept; no _ensure_lldb_installed, which antalya-26.8 does not have). Docstring keeps the 'liveness check' call site that exists on antalya-26.8. ci/tests/test_print_stacktraces.py: kept deleted, as on antalya-26.8, where 3a69545 removed all of ci/tests/. The PR's test changes to that file are therefore not carried over. Source-PR: #2385 (#2385)
…next commit) --- Original cherry-pick message follows: Merge pull request #2292 from Altinity/enable-cas-s3-stress Enable cas s3 stress # Conflicts: # .github/workflows/master.yml # .github/workflows/pull_request.yml # ci/tests/test_stress_job.py
Kept the antalya-26.8 `needs` lists of `finish_workflow` in `master.yml` and `pull_request.yml` and added only the four `stress_test_amd_*_cas_s3_storage` jobs introduced by the source PR. Kept both `ensure_ripgrep_installed` and `get_additional_envs` imports in `ci/tests/test_stress_job.py`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Source-PR: #2292 (#2292)
…next commit) --- Original cherry-pick message follows: Merge pull request #2444 from Altinity/docs/antalya-26.6/metrics-and-events Document Antalya-only profile events and metrics. # Conflicts: # src/Common/ProfileEvents.cpp
…next commit) --- Original cherry-pick message follows: Merge pull request #2511 from Altinity/fix/antalya-26.6/cas-renewal-memory-guard Keep the CAS mount lease renewal alive over the server memory limit # Conflicts: # src/Disks/DiskObjectStorage/MetadataStorages/ContentAddressed/Pool/CasMountRuntime.cpp
ThreadGroup::parent (ThreadStatus.h) already retains the parent group for the raw tracker pointers; this member held a second shared_ptr to the same object and was never read. The constructor parameter takes the member's old name, and the two comments that carried a fork ticket tag go. Introduced by 002f387 (PR #2159), ported in c770be9. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
MetadataStorageFromCacheObjectStorage is never built over a content-addressed storage (DiskObjectStorageCache.cpp reuses that storage directly), REPLICATION_PROTOCOL_VERSION_WITH_CA_RELINK gates nothing (the DataPartsExchange comment that explained it is rewritten around what remains), CASConditionalWriteFenceLostPostWrite is never incremented, and MergeTask's projection_uses_parent_transaction is written and read inside one function. Introduced by edb79be, 9fd2b91 and 93d545b (PR #2159), ported in c770be9. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
Comments and one exception message carried fork-internal ticket tags (B16, B33, B34, B59, B168 and others) and history; the constraints they explain stay as one line each where the code still relies on them, and a comment that only restated history goes. No code change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
The rustfs bucket step called /mc; the 26.8 stateless-test image ships the client at /opt/aistor/mc, so the job failed at "Start ClickHouse Server" with "/mc: No such file or directory". Probe both locations and fail the step when neither exists instead of trying a bare mc from PATH. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
checkAlterPartitionIsPossible kept a whitelist of partition commands for CAS disks because early clones copied part files one by one outside a transaction. Every command now clones through freeze, freezeRemote or clonePart, which publish the clone as one part, and EXPORT PART only reads the source (ExportPartTask over MergeTreeSequentialSource), so the whitelist and its history-laden comment go; one comment at the case says what a new command type must not do. EXPORT PARTITION persists `partition_exports/<key>.json` in the table directory. The CAS path parser took that directory for a part named `partition_exports`, so the scheduler's tmp + replaceFile sequence ran as two part publications. The name is reserved as a table-level directory next to `deduplication_logs`; the descriptor is then a verbatim table-level file and the scheduler is unchanged. The EXPORT PART and EXPORT PARTITION tests lose their no-cas-storage tag and run on the CAS lanes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
`appliesOperationsEagerly` replaces `transactionIsStagingOverlay`: one flag says that a mutating method runs against the metadata transaction at call time and never enters operations_to_execute; the memory storage, the content-addressed storage and the path wrapper report it. A non-empty queue at commit or tryCommit is a LOGICAL_ERROR whose message names the flag (the chassert is a no-op in release). The flag implies nothing about visibility or rollback; the in-flight read API (`tryGetInFlightStorageObjects` and friends) is independent of it, and its header comments say so. Four gtests over the memory storage pin the contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
A committed ref changes owner with one `Move` record instead of a manifest republish. Logs carrying a Move are stamped generation 2 (`G_REF_MOVE`); the pool records `min_reader_generation`, and a generation-1 build refuses to mount a pool whose floor is above its ceiling. The raise 1 -> 2 happens in `Pool::open` right after a writable mount (one-way, logged as WARNING); a read-only mount never raises it, and `admitOrValidate` no longer touches the floor. A pool created by a 26.8 writable mount is born at 2, and only a writable mount may admit a new hash algorithm (`allow_new = blob_hash_allow_new && !read_only`); the decommission tool admits none. Every node must run 26.8 before the first writable mount; there is no per-node switch. `currentReadCeiling` is the single reader gate, the pool-meta floor check included; a `static_assert` ties the ref-log history to `G_REF_MOVE`. `classifyOwnerTransitionShape` lives in Formats/CasRefLogFormat and names five shapes (AddPrecommit, RemovePrecommit, RemoveCommitted, Promote, Move). Ownership conflicts get their own error code, `CAS_OWNERSHIP_CONFLICT` (1039): `ABORTED` is read by the replication queue as a cancelled entry. Every throw goes through `Cas::throwCasOwnershipConflict`, which counts it in `ProfileEvents::CASOwnershipConflict`; `CASRefMove` counts owner moves. A ref-log mutation that renames a ref scopes exactly its two names (`MutationScope::forEachName`/`namesForLog`), so co-batching stays possible and `confirmExactRef` refuses only those names instead of the whole namespace. Housekeeping that came with it: `rmwPoolMeta` is the one read-modify-write skeleton behind `admitOrValidate` and `raiseMinReaderGeneration`; the absent-meta case of the raise is a `LOGICAL_ERROR` like admission; `Cas::nowMs` (Primitives/CasClock.h) and `makeSetPublishedAtOp` give the ledger and the later rename path one clock and one builder. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
A committed part on a content-addressed disk is renamed by one owner Move plus SetPublishedAt in the same append (modification time follows the rename, as on a local disk); detached/ and moving/ take the same path, cross-namespace renames keep republication. `renameCommittedRef(src, dst, expected)` owns the decision: Move inside a namespace when the destination is free, `republishRef` otherwise (another namespace, or a destination an earlier attempt already published), and `CAS_OWNERSHIP_CONFLICT` when `src` no longer names the manifest the caller read. `moveRefExact`, `dropRefExact` (returning `Cas::Removal`) and `dropRefIfMatches` (its noexcept rollback wrapper) are the part-level primitives. `partition_exports` descriptors route as table-level files through the same transaction (comments at the two sites). A rewrite of a committed part records the manifest it started from (`base_manifest_ref`) and `publishStaging` repoints only while the ref still names it; otherwise `CAS_OWNERSHIP_CONFLICT`. A committed manifest with no top-level file is a nested scratch storage (text index), not a part: `publishStaging` drops it and publishes afresh instead of merging into it. A committed part edited and renamed in one transaction keeps its untouched files: the staged source's base is renamed first (conditional on that base) and becomes the destination staging's base, so the delta merges under the new name as it does in place. A part another writer published under the staged name, or a base dropped meanwhile, is a conflict, never a silent drop or a delta-only part. `PublishPolicy` is the expected manifest or nothing: unset = first commit (another committed manifest is refused with the write-retry-later error), set = repoint while the ref still names it (`CAS_OWNERSHIP_CONFLICT` otherwise). `repointRef` requires the expected manifest; the flag/optional pair it replaces encoded the same three outcomes. Audit events after a durable append are best-effort: `emitAuditBestEffort` builds the `CasEvent` inside its guard after the sink check and locks the memory tracker in the handler, so an allocation failure after a landed Move or drop neither fails the caller nor terminates. `PoolAccessSnapshot` moves next to the private `poolAccess()` it belongs to. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
…e the parts lock MergeTree writers return to the upstream text: the storage transaction commits right after finalize (the sinks, the plain and replicated merge and mutation tasks, `MergeFromLogEntryTask` included), `renameParts` only renames, and the fork's writer guards and the comment block that tied disk durability to the Keeper commit order go. On a content-addressed disk the rename of a committed part is one owner Move, so a merge or mutation publishes once and moves once; a replicated merge renames inside the open transaction and publishes once under its final name. `StorageMergeTree::createEmptyParts` (TRUNCATE, DROP PART/PARTITION, MOVE PARTITION TO TABLE) left the storage transaction open, so the publish ran inside `Transaction::commit` under `DataPartsLock`; it now commits right after creation, like the sinks. `Transaction::commit` throws `LOGICAL_ERROR` when a part on a content-addressed disk still has an open storage transaction: every writer commits it before the parts lock, and the loop that used to do it silently hid two misses. Two pauseable fail points (`part_storage_pause_before_commit_transaction`, `part_storage_pause_after_commit_transaction`) let tests hold a part before and after its storage commit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
Publish-count oracles over `system.cas_log` for INSERT, merge, mutation (including an untouched-part mutation), LWU, ATTACH FROM, MOVE, DETACH/ATTACH, TRUNCATE, text-index merge and packed projections; a restart between the storage commit and the rename (test_cas_early_commit_restart); a generation-1 build refusing a pool whose reader floor is 2 (test_cas_ref_move_compat). 05387 pins that a replicated merge publishes once under its final name and moves nothing, unlike the plain merge in 05388; 05054's part-removal oracle counts the owner move a merge now makes. The tests count publishes per operation, so they run with fixed settings (no-random-settings, no-random-merge-tree-settings). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
configuration.md: upgrading a pool from 26.6.x (the floor raise at the first writable mount, the refusal line a generation-1 build logs, no downgrade after the raise). architecture: the five owner-transition shapes, rename = Move inside a namespace and republish across (or when the destination already exists), commit-then-Move in the part lifecycle. operations/debugging.md: what `CAS_OWNERSHIP_CONFLICT` means and how to find the `ref_move` row that carries the destination name; the decommission tool never admits a new hash algorithm; the GC-upgrade section says the floor cannot be downgraded. metrics_and_events.md: `CASRefMove`, `CASOwnershipConflict`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
…-disk-transactions-v2-merge Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com> # Conflicts: # .github/workflows/master.yml # .github/workflows/pull_request.yml
PR #2647 Distributed-Systems AuditVerdictNo High defect. Three findings: two Medium in version skew, one Low in timing. The central mechanism is sound. A committed rename inside one namespace becomes a single What is not fully closed is the mixed-version story. The pool's Separately, reserving What the change does
Model
The central argument, and why it holdsThe Move is conditional on what the caller read, at the moment it is written. The Move is net-zero for GC. Durability still precedes the Keeper decision. The deleted comment block in The two-name scope was widened everywhere it is read. A Move is safer for fetch-by-relink than the republish it replaces. A relink receiver adopts the sender's blobs by evidence of the sender's live edge. A FindingsConfiguration and version skewMedium: the reader-generation floor gates new mounts, not peers that are already mounted — and a 26.6.x peer's GC then stalls permanently with only a log line.
Medium:
Timing and orderingLow: the rename restamps
What was checked and found cleanWorth recording, because several of these are the places a change of this shape usually breaks.
Coverage
Assumptions and limits. Static reading of Operational readinessScope. Upgrading and running a CAS pool at head 1. A 26.6.x server left running during the upgrade silently stops reclaiming storage
2. The point of no return is a side effect of starting a server
3.
|
PR #2647 CI VerificationVerdictDoes not pass CI verification. Two failures must be fixed before approval, and two more are unresolved. Nothing here points at a defect in the server. The two blocking failures are both self-inflicted by this PR's own test material, and both have a known fix:
Two jobs were still running at the time of this review: How the list was reducedSix checks are red. No cascade failures: no job shows a server death or a killed process group, and each red job names its own failing tests.
Findings
|
| Step | Result |
|---|---|
| Re-run with the same randomized settings | 25 runs, 25 failed |
| Re-run without randomized settings | 39 runs, 0 failed |
| Minimized culprit | --min_bytes_for_full_part_storage 291743465 |
That setting pushes the part onto packed storage. part_storage_fail_commit_transaction is injected in DataPartStorageOnDiskFull::commitTransaction only; DataPartStorageOnDiskPacked::commitTransaction has no fiu_do_on, so the failpoint never fires, the insert succeeds, and the expectation fails.
The fix already exists on the base PR. Commit 267672c528e, "Apply part commit failpoint to packed storage", is the current head of #2579 and adds exactly that fiu_do_on to DataPartStorageOnDiskPacked.cpp. This branch is built on an older #2579 head and does not contain it.
Action: rebase onto the current #2579 head, or tag the test with a fixed min_bytes_for_full_part_storage so the randomizer cannot select packed storage. The server behaviour under test is correct either way — nothing here suggests the commit-ordering change is wrong.
test_cas_ref_move_compat::test_old_pool_floor_and_old_reader — regression
Job: Integration tests (amd_asan_ubsan, db disk, old analyzer, 1/8), 120268328007.
Also new in this PR. It fails at line 55:
assert new.contains_in_log("min_reader_generation raised 1 -> 2")
E AssertionError: assert False
The server did the right thing; the assertion looks in the wrong file. The preceding assertion, _count(new, "event_type = 'ref_move'") == 1, passed — which already proves the floor was at 2, because moveRefExact throws LOGICAL_ERROR when refMoveAllowed() is false. The artifacts confirm it directly. In new/logs/clickhouse-server.log.0.gz:
2026.10.23 07:37:41.096944 [ 202 ] <Warning> CasPool: Content-addressed pool '4030f6cad8fc23f14a1eaa0bd307d353':
min_reader_generation raised 1 -> 2; generation-1 readers can no longer mount this pool
Two things compound. The new instance already has storage_conf.xml at cluster start — contrary to the test's own comment, "No CAS disk at start: the pool must be created by the old build first" — so it mounts the pool and raises the floor at 07:37:41, during its first boot. Then restart_clickhouse() rotates the log, the raise line moves to clickhouse-server.log.0.gz, and contains_in_log reads only clickhouse-server.log.
Action: either keep the CAS disk genuinely off the new instance until after the old build has created the pool, or make the assertion read rotated logs. No product change needed.
This test is worth getting green rather than deleting: its old node's log also contains the permanently-held GC rounds described in finding 1 of the audit, which is the real version-skew gap.
test_storage_delta and test_storage_s3 — infrastructure
Jobs: Integration tests (amd_asan_ubsan, db disk, old analyzer, 6/8) 120268328035 and 1/8 120268328007.
Every row is an ERROR in module setup, not a test assertion:
Exception: Command [docker compose … pull] return non-zero code 18:
zoo2 Skipped - Image is already being pulled by node_with_environment_credentials
Exit 18 from docker compose pull, with the concurrent-pull "Skipped" messages, is registry contention during image fetch. Neither module touches CAS, and the diff changes no Dockerfile, image or package list. Rerun.
04812_clickhouse_disks_move_directory — unknown
Job: Stateless tests (amd_debug, parallel), 120266603873. The only named failure in a job that otherwise reports 13,514 passed.
Output mismatch (return code 83) on the missing-parent case: clickhouse-disks move src aa/bb/cc leaves src in place on test_disk_04812 but creates aa/cc on test_disk_plain_rewritable_04812.
The test exists unchanged on antalya-26.8 and is not touched by the series, which modifies neither clickhouse-disks, the local disk, nor plain-rewritable metadata. That points at pre-existing, but "points at" is not evidence: this needs the branch fail rate for this test on this job before it can be called pre-existing-flaky. Marked unknown deliberately.
test_cas_gcs::test_ordinary_goog4_traffic_keeps_upstream_semantics and test_storage_gcp_auth::test_gcp_auth_ordinary_contract — unknown
Job: Integration tests (amd_asan_ubsan, db disk, old analyzer, 3/8), 120268327952. Two failures in 978 tests.
test_cas_gcs/test.py:866: assert "if-match" not in headers, record
The assertion is that ordinary, non-CAS GCS traffic carries no conditional-write header. On this head it does.
The 13-commit series cannot be the cause: its only changes under src/IO/S3/ are comment and whitespace edits. Client.cpp loses a four-line comment above an unchanged isPreconditionFailedError early return; ReadBufferFromS3.cpp loses a comment; copyS3File.cpp loses two blank lines; S3ObjectStorage.cpp drops two unused declarations and folds a local into a return. No request is constructed differently.
The likely source is the CAS port this branch inherits — test_cas_gcs/test.py does not exist on antalya-26.8 at all, so it arrived with that lineage. Confirming that needs the fail rate for both tests on the base branch and on sibling CAS PRs. Marked unknown.
Worth saying plainly: if this is real, a conditional header on ordinary S3/GCS traffic is a correctness problem for non-CAS users, so it deserves a definite answer rather than a shrug.
DCO — policy, not a test
Every commit in the series carries Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>, and every one also carries Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> with no matching sign-off. DCO requires one sign-off per author, co-authors included. Either add Signed-off-by: Claude Fable 5.1 <noreply@anthropic.com> to the four affected commits or drop the co-author trailer.
Recommendation
Do not approve on CI grounds yet. Three things, in order:
- Fix
05014— rebase onto the current #2579 head (which carries the packed-storage failpoint) or pinmin_bytes_for_full_part_storagein the test. - Fix
test_cas_ref_move_compatso the assertion sees the log line the server already writes. - Resolve the three
unknownrows against the CI database, starting with the two GCS tests.
Then rerun the two integration shards whose image pulls failed, and wait for Unit tests (asan_ubsan) and the CAS regression lane, which had not finished.
…ails on unreachable objects `DisksApp` returned the error code of the last failing command of a non-interactive `--query`, for every command. Upstream's `04812_clickhouse_disks_move_directory` runs failing commands on purpose and expects exit 0, so it was red on this branch; the raw code also wrapped modulo 256 (256, 512, 768 read as 0). `ICommand::exits_nonzero_on_error` (set by `cas-fsck`, `cas-gc-dryrun`, `cas-gc-rebuild`, `cas-inspect`, `cas-drop-member`) makes the process exit 1 when such a command throws, whatever runs after it; other commands, an unknown command and a parse error keep the "always 0" contract. `cas-fsck` now also throws on `unreachable > 0` for a complete scan (`--namespace` and `--partial` scans classify orphans as unreachable by construction and stay silent). The contract is in `docs/en/antalya/cas/operations/debugging.md`. Test 05393: clean pool exits 0; an unreferenced blob copy exits 1; a missing referenced blob exits 1; `remove` of a missing path on a local disk exits 0; `cas-fsck; list .` exits 1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
`part_storage_fail_commit_transaction` and the two pause fail points were injected only in `DataPartStorageOnDiskFull::commitTransaction`. With a randomized `min_bytes_for_full_part_storage` the part is Packed, the fail point never fires and `05014_insert_dedup_disk_commit_failpoint` sees the INSERT succeed instead of `FAULT_INJECTED` (CI: Stateless amd_asan_ubsan, db disk, PR 2647). The Packed storage now injects the same seams before and after `transaction->commit()`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
… fail-point test `report.unreachable` counts every present blob no ref names, which includes the GC pipeline (`pending_gc`, `awaiting_gc`) and an in-flight build's not yet adopted blobs (`unaccounted`), so exiting 1 on it made a healthy pool under load fail `cas-fsck`. The verdict is back to the hard findings (`dangling`, `chain_broken`, `corrupted_runs`, `lifeless_keys`); the command description and `debugging.md` name them. 05393 now also asserts the message of each case; its unreferenced-blob case expects exit 0 with `unreachable=1` in the summary. 05397 is the Packed twin of 05014 (a Packed part on a local disk, `part_storage_fail_commit_transaction`, `FAULT_INJECTED`, the retry inserts), so the Packed fail points have a test that does not depend on randomized settings; the pause-after fail point sits after the state reset as in the Full storage. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
Both instances loaded `configs/backups.xml`, so they claimed the same `cas_server_root_id` on the same rustfs prefix; whichever server started second refused to start ("CAS server-root 'itest-database-backup' was claimed by a different server during our claim (foreign owner)") and every test on `instance` timed out waiting for it (Integration tests amd_asan_ubsan 6/8, 26 failures, red on PR 2579 as well). The second instance came with the backport of ClickHouse#118674. `instance_local_metadata` now loads `backups_local_metadata.xml`: the same disks with a distinct root id and prefix.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
…s stopped
The test copied the CAS disk config into a running server's config.d and then restarted it. Under ASan the config reloader mounted the pool first and raised the reader floor in that process — whose log the restart discards — so the restarted process found floor 2, raised nothing, and `contains_in_log("min_reader_generation raised 1 -> 2")` failed (Integration tests amd_asan_ubsan 1/8 on PR 2647). Stop, copy, start: the first writable mount, and its log line, belong to the process the test reads.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
Upstream pins every S3/GCS read to the ETag captured at setup (`s3_validate_etag_on_read`, default on; "Detect concurrent in-place overwrite of S3/GCS objects during read"), so ordinary GOOG4 and OAuth traffic now carries `If-Match` on GET/HEAD. `test_ordinary_goog4_traffic_keeps_upstream_semantics` and `test_gcp_auth_ordinary_contract` asserted the 26.6 contract and were red on PR 2579 as well. They now forbid the header on writes only; conditional writes stay CAS-only, and the other forbidden headers are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
…ot in the parent `DiskObjectStorageOverMemoryMetadataTest::SetUp` constructs a `DiskObjectStorage`, whose constructor starts a `BlobKillerThread` on `Context::getSchedulePool()`: a `BackgroundSchedulePool` with 16 threads in the global thread pool that nothing stops for the rest of the process. gtest runs every `*DeathTest` suite before all others, so in sanitizer builds (the only ones that compile the death variants) the two eager-contract death tests ran that `SetUp` first, and `ThreadPool.GlobalFull1`'s `global_pool.wait()` then waited forever: the ASan unit-test job of PR 2647 timed out after 5 h (release builds compile the death tests out, hence green locally; the base has no such death tests). The death tests are plain `TEST`s now: the disk is built by `makeDiskOverLocal` only inside the `EXPECT_DEATH` statement, i.e. in the child that dies; the parent never starts the pool. The fixture uses the same helper. Reproduced with the CI ASan binary: `DiskObjectStorageOverMemoryMetadataDeathTest.*:ThreadPool.GlobalFull1` hangs (with or without `ThreadFuzzer.mutex` in between), the non-death sibling suite does not; gdb shows `GlobalFull1` in `ThreadPool::wait` with 16 `BgSchPool` threads created from `BlobKillerThread` in `SetUp`. CI: #2647 (Unit tests asan_ubsan). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Mikhail Filimonov <mfilimonov@altinity.com>
…-disk-transactions-ci-merge
Renaming a committed part on a content-addressed disk is one ref-log owner
Move(plusSetPublishedAtin the same append) instead of a manifest republish; MergeTree writers return to the upstream text (storage transaction committed right afterfinalize,renamePartsonly renames), and the fork's five writer guards go. Logs carrying aMoveare stamped format generation 2; the pool recordsmin_reader_generation, which a 26.8 writable mount raises once (26.6.x then refuses to mount). The branch starts with reverts of fork-only changes to upstream files that 26.8 made redundant, then allowsEXPORT PART/EXPORT PARTITIONon CAS disks (partition_exportsis a reserved table-level directory, so the descriptor is not parsed as a part). EveryCAS_OWNERSHIP_CONFLICT(1039) is counted inCASOwnershipConflict; the operator section is indocs/en/antalya/cas/operations/debugging.md.The branch is built on the head of #2579 (packed parts), so this PR carries those commits until 2579 merges; the series itself is the 13 commits starting at "Replace unwrapTableProxy with castStorage in SYSTEM commands".
Measured on local object storage: INSERT/merge/mutation/LWU/ATTACH FROM/fetch
manifest_put2→1,ref_drop1→0,ref_move1; INSERTPartsLockHoldMicroseconds2557→317. A replicated merge renames inside the open storage transaction and commits once under the final name (manifest_put1,ref_move0, oracle in 05387).Related: #2579 (base: packed parts)
Related: #2159 (the CAS port the reverts refer to)
Related: #2540
Follow-up: gate fetch-by-relink on a capability cookie instead of replication protocol version 11.
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
Content-addressed disks rename a committed part with a single ref-log record instead of republishing its manifest. A pool mounted writable by 26.8 can no longer be mounted by 26.6.x: upgrade every server before the first writable mount.
EXPORT PARTandEXPORT PARTITIONwork on CAS disks.Documentation entry for user-facing changes
docs/en/antalya/cas/configuration.md(upgrading a pool from 26.6.x),docs/en/antalya/metrics_and_events.md(CASRefMove,CASOwnershipConflict),docs/en/antalya/cas/operations/debugging.md(CAS_OWNERSHIP_CONFLICT).CI/CD Options
Exclude tests:
Regression jobs to run:
🤖 Generated with Claude Code