Skip to content

Antalya 26.6: Implement TRUNCATE TABLE for Iceberg Engine (REST … - #2125

Merged
zvonand merged 6 commits into
antalya-26.6from
feature/antalya-26.6/pr-1655
Aug 12, 2026
Merged

Antalya 26.6: Implement TRUNCATE TABLE for Iceberg Engine (REST …#2125
zvonand merged 6 commits into
antalya-26.6from
feature/antalya-26.6/pr-1655

Conversation

@zvonand

@zvonand zvonand commented Jul 29, 2026

Copy link
Copy Markdown
Member

Changelog category (leave one):

  • New Feature

Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):

Frontport for Antalya 26.3

  • 26.1 Antalya port - Implement TRUNCATE TABLE for Iceberg Engine (REST catalog support) Feature: Support TRUNCATE TABLE for Iceberg engine #1529, It's a frontport from 26.1, contains:
    • feat(iceberg): Implement TRUNCATE TABLE for Iceberg Engine (REST catalog support) — Core implementation: metadata-only truncation generating a new overwrite snapshot with empty manifest list, committed atomically via REST catalog
    • fix(iceberg): pass new_snapshot to updateMetadata in IcebergStorageSink — Fixed silent breakage of all INSERTs on REST catalog tables (wrong JSON object passed to catalog->updateMetadata)
    • fix(iceberg): restore return false in RestCatalog::updateMetadata — Preserve retry contract; add LOG_WARNING for diagnostics
    • fix(iceberg): revert Mutations.cpp updateMetadata to pass new_snapshot — Same fix as IcebergStorageSink, applied to ALTER TABLE DELETE/UPDATE path
    • refactor(iceberg): add comment explaining Avro zigzag encoding — Reviewer-requested documentation for manual Avro OCF serialization
    • refactor(iceberg): address code review feedback on TRUNCATE implementation — Named zero arguments, helper functions, restart integration test (feature (iceberg): Implement TRUNCATE TABLE for Iceberg Engine (REST … #1655 by @il9ue).

CI/CD Options

Exclude tests:

  • Fast test
  • Integration Tests
  • Stateless tests
  • Stateful tests
  • Performance tests
  • All with ASAN
  • All with TSAN
  • All with MSAN
  • All with UBSAN
  • All with Coverage
  • All with Aarch64
  • All Regression
  • Disable CI Cache

Regression jobs to run:

  • Fast suites (mostly <1h)
  • Aggregate Functions (2h)
  • Alter (1.5h)
  • Benchmark (30m)
  • ClickHouse Keeper (1h)
  • Iceberg (2h)
  • LDAP (1h)
  • Parquet (1.5h)
  • RBAC (1.5h)
  • SSL Server (1h)
  • S3 (2h)
  • S3 Export (2h)
  • Swarms (30m)
  • Tiered Storage (2h)

Cherry-picked from #1655.


Adaptation notes for antalya-26.3

The following API changes between antalya-26.1 and antalya-26.3 required adaptation:

  • RestCatalog constructor signature updated to match antalya-26.3 calling convention in DataLakeConfiguration.h
  • DatabaseDataLakeSetting member names updated
  • Cargo.lock restored to antalya-26.3 version (our changes do not touch any Rust code)

All feature logic is identical to the merged antalya-26.1 implementation.

feature (iceberg): Implement TRUNCATE TABLE for Iceberg Engine (REST …
@zvonand zvonand added releasy Created/managed by RelEasy antalya-26.6 labels Jul 29, 2026
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

Workflow [PR], commit [dfeeb88]

@zvonand zvonand added the forwardport This is a frontport of code that existed in previous Antalya versions label Aug 4, 2026
@zvonand zvonand changed the title Antalya 26.6: feature (iceberg): Implement TRUNCATE TABLE for Iceberg Engine (REST … Antalya 26.6: Implement TRUNCATE TABLE for Iceberg Engine (REST … Aug 4, 2026
@zvonand

zvonand commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

@blau-ai

@blau-ai

blau-ai commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

CI triage

Verdict: 2 failing checks — both PR-caused, same root cause. Nothing flaky/infra/pre-existing.

Both red checks (Build (arm_tidy) and Fast test) fail at compile time on the same code. Every other job shows skipping because it is gated behind the build — those are not independent failures. This is a backport of #1655 (written against antalya-26.3), and the Iceberg-writes API on antalya-26.6 has diverged, so the ported code no longer compiles here.

There are two distinct compile problems:

1. Duplicate Iceberg field aliases in Constant.h — blocks the whole build (hit first, e.g. by Fast test)

Constant.h:145:1: error: redefinition of 'f_deleted_records'
Constant.h:126:1: note: previous definition is here
Constant.h:144:1: error: redefinition of 'f_deleted_data_files'
...
2 errors generated. → ninja: build stopped: subcommand failed.

antalya-26.6 already defines both aliases (origin/antalya-26.6:Constant.h:142-143, now at :144-145). The PR re-adds them at :126/:128, so they collide. Fix is to drop the two lines the PR added:

--- a/src/Storages/ObjectStorage/DataLakes/Iceberg/Constant.h
+++ b/src/Storages/ObjectStorage/DataLakes/Iceberg/Constant.h
@@ -123,9 +123,7 @@ DEFINE_ICEBERG_FIELD_ALIAS(partition_specs, partition-specs);
 DEFINE_ICEBERG_FIELD_ALIAS(spec_id, spec-id);
 DEFINE_ICEBERG_FIELD_ALIAS(added_records, added-records);
-DEFINE_ICEBERG_FIELD_ALIAS(deleted_records, deleted-records);
 DEFINE_ICEBERG_FIELD_ALIAS(added_data_files, added-data-files);
-DEFINE_ICEBERG_FIELD_ALIAS(deleted_data_files, deleted-data-files);
 DEFINE_ICEBERG_FIELD_ALIAS(added_delete_files, added-delete-files);

2. IcebergMetadata::truncate() uses the 26.3 FileNamesGenerator / MetadataGenerator API (arm_tidy, 5 errors)

IcebergMetadata.cpp:653:30: error: no matching constructor for initialization of 'FileNamesGenerator'
    note: candidate constructor not viable: requires 4 arguments, but 5 were provided
IcebergMetadata.cpp:659:30: error: no matching constructor for initialization of 'FileNamesGenerator'
IcebergMetadata.cpp:667:70: error: no member named 'generateMetadataName' in 'DB::FileNamesGenerator'
235321 warnings and 5 errors generated.

On antalya-26.6 the writer API changed:

  • Constructor is 4-arg, FileNamesGenerator(table_location, bool use_uuid_in_metadata, compression, format) (FileNamesGenerator.h:32). There is no config_path / no separate transactional-path argument — truncate() passes 5 args. The data-path override is now set separately via setDataLocation().
  • generateMetadataName() no longer exists — use generateMetadataPathWithInfo(), which returns a struct with a .path (IcebergPathFromMetadata) (FileNamesGenerator.h:47).
  • generateNextMetadata(...) returns NextMetadataResult{ snapshot, manifest_list_path } — only 2 fields (MetadataGenerator.h:22-29), but truncate() unpacks 3 ([new_snapshot, manifest_list_name, storage_manifest_list_name]). Storage paths are now obtained by running the metadata path through IcebergPathResolver::resolve(...).

Good news: the is_truncate=true overload of generateNextMetadata already exists on this branch (MetadataGenerator.h:43), so no MetadataGenerator changes are needed — only the caller in truncate().

Concrete fix: rewrite the FileNamesGenerator setup and path handling in IcebergMetadata::truncate() to follow the pattern this branch's writer already uses:

  • construction + setDataLocation() — see IcebergWrites.cpp:738-748;
  • generateMetadataPathWithInfo()generateNextMetadata()resolver.resolve(manifest_list_path) — see IcebergWrites.cpp:952-970.

Note: clang-tidy stops reporting after 5 errors, so once the constructor/generateMetadataName calls are fixed, expect to reconcile a couple of follow-on mismatches in the same function (the 3-vs-2 structured binding above, and the generateManifestList / storage-path arguments). The rest of the diff (Context::createCopy, the new IDataLakeMetadata::truncate virtual, IcebergWrites.cpp zigzag helper) compiled cleanly.


I can prepare these fixes — the trivial Constant.h dedup is safe, and I can adapt truncate() to the 26.6 API as above (I can't build locally, so CI on the resulting PR would be the validation). Do you want me to open a blau/* PR with the fix, or commit directly to feature/antalya-26.6/pr-1655? I'll hold until you confirm the delivery.

Evidence: run 30468779903 — jobs Build (arm_tidy) and Fast test, head 7a636452.

@zvonand

zvonand commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

@blau-ai commit the fix directly to this branch

)

The #1655 backport was written against the antalya-26.3 Iceberg writer API
and did not compile on antalya-26.6, breaking Build (arm_tidy) and Fast test:

* Constant.h re-added the `deleted_records` / `deleted_data_files` field
  aliases that antalya-26.6 already defines, causing macro redefinition
  errors. Drop the duplicate definitions.

* IcebergMetadata::truncate() used the 26.3 FileNamesGenerator /
  MetadataGenerator / generateManifestList signatures. Rewrite it to follow
  the antalya-26.6 writer (IcebergStorageSink in IcebergWrites.cpp):
  - 4-arg FileNamesGenerator over resolver.getTableLocation();
  - generateMetadataPathWithInfo() instead of the removed generateMetadataName();
  - generateNextMetadata() now returns {snapshot, manifest_list_path}; resolve
    storage paths via IcebergPathResolver::resolve() and the catalog path via
    resolveForCatalog();
  - generateManifestList(resolver, ...) with empty entry/size vectors.

Metadata-only truncate semantics are unchanged. Not built locally; validated
by CI on this PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@blau-ai

blau-ai commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Done — committed the fix directly to feature/antalya-26.6/pr-1655 as requested.

Commit: 984609cFix antalya-26.6 build: adapt Iceberg TRUNCATE to 26.6 writer API

What changed (both compile errors from the triage above)

1. Constant.h — macro redefinition (blocked the whole build)
Dropped the two field aliases the backport re-added (deleted_records, deleted_data_files); antalya-26.6 already defines both further down the file.

2. IcebergMetadata::truncate() — 26.3 writer API no longer exists (arm_tidy)
Rewrote the path/metadata handling to follow this branch's writer (IcebergStorageSink, IcebergWrites.cpp) so REST (transactional) and non-transactional catalogs go through the same IcebergPathResolver:

  • 4-arg FileNamesGenerator(resolver.getTableLocation(), is_transactional, compression, format) — the old 5-arg config_path form is gone; the data-path override is now a separate setDataLocation() concern (a no-op for a metadata-only truncate, which writes no data files);
  • generateMetadataPathWithInfo() replaces the removed generateMetadataName();
  • generateNextMetadata(...) now returns {snapshot, manifest_list_path} (2 fields, was 3) — storage paths come from resolver.resolve(...), and the catalog path from resolver.resolveForCatalog(...);
  • generateManifestList(resolver, …, {}, new_snapshot, {}, …) — first arg is the resolver (was the generator), and the empty manifest-entry/size args are now typed vectors.

The metadata-only truncate semantics (empty overwrite snapshot superseding all previous snapshots) are unchanged; the is_truncate=true overload of generateNextMetadata already exists on this branch, so no MetadataGenerator change was needed.

Validation

I can't build ClickHouse in this environment, so this is validated by CI on this PR, not locally. The two red checks (Build (arm_tidy), Fast test) both failed at compile time on exactly these two spots; a fresh run should now get past compilation. I'll be watching — ping me if anything downstream (e.g. the follow-on mismatches clang-tidy would have hidden behind its 5-error cap) still trips.

@zvonand zvonand mentioned this pull request Aug 7, 2026
55 tasks
@mkmkme

mkmkme commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

The issue is reproducible locally. Will investigate and try to fix

mkmkme and others added 2 commits August 11, 2026 21:34
`test_iceberg_truncate.py` was written against `antalya-26.1`, where
`docker_compose_iceberg_rest_catalog.yml` shipped its own `minio` service on
port 9000 and published the REST catalog on the fixed host port 8182. Two
later commits removed both:

- `bba8284cd0b` replaced the fixed host port 8182 with a port allocated from
  the pool and exposed as `cluster.iceberg_rest_catalog_port`.
- `48a014af159` dropped the per-catalog `minio`/`mc` services, so catalogs now
  use the standard `minio1` on port 9001.

Both commits adapted the sibling test `test_read_in_order_with_pyiceberg.py`,
but `test_iceberg_truncate.py` was merged in afterwards without the same
adaptation, so it still referenced the deleted container and failed with
`docker.errors.NotFound: No such container: ...-minio-1` in
`get_instance_ip`.

Derive the catalog URI from `cluster.iceberg_rest_catalog_port`, take the S3
endpoint from `cluster.minio_ip`/`cluster.minio_port`, and point
`storage_endpoint` at `minio1:9001`, matching the sibling test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zvonand

zvonand commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

@blau-ai

@blau-ai

blau-ai commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

CI triage for #2125

Verdict: of all the red checks, only one is actually caused by this PR — the DCO sign-off. Everything else is pre-existing on antalya-26.6, environmental/infra, or regression-suite-vs-build version skew. None of the test failures touch the Iceberg TRUNCATE code paths this PR adds.

Summary: 1 PR-caused (DCO), rest not (0 test regressions attributable to this PR).


🔴 PR-caused (fix required)

DCO — one commit is missing its Signed-off-by line:

Fix (must be done by a human — I can't force-push). Since this is your own branch and only that commit needs it:

git rebase HEAD~6 --signoff
git push --force-with-lease origin feature/antalya-26.6/pr-1655

Or, to avoid rewriting history, add a DCO remediation commit. Going forward, git commit -s adds the trailer automatically.


🟢 Not caused by this PR

Stateless tests (amd_debug parallel, amd_debug distributed/s3, arm_binary parallel) — the single real [ FAIL ] in each is the same test:

04337_iceberg_v3_row_lineage_reserved_field_idresult differs, actual output is missing the trailing ICEBERG_SPECIFICATION_VIOLATION line (the reader didn't reject an unmapped, non-reserved field id 2147483447).

This is a reader-side reserved-field-id boundary check. This PR does not touch it — it only modifies the Iceberg write / TRUNCATE path (IcebergWrites.cpp, MetadataGenerator.cpp) plus include/extern additions. Proof it's pre-existing: the test was merged into antalya-26.6 on 2026-08-11 (backport #2198 / upstream ClickHouse#107377) and the identical [ FAIL ] reproduces on unrelated PR #2197 (cluster-discovery, no Iceberg changes) and on the current base-branch tip run. Likely the reader-side fix from ClickHouse#107377 didn't fully land with the test — worth a separate issue against the backport, not this PR.
The other non-green lines in those jobs are infra: 03441_deltalake_* and 04070_url_base_setting are [ BROKEN ] = "INVESTIGATE - S3 sometimes unreachable" / "fragile timeout on debug" (auto-excluded, don't gate).

Regression — Iceberg (1)/(2), release & aarch64 — the failing features are all feature support validation gates for features not present in this build, i.e. the regression suite is newer than the 26.6 build:

  • Swarm object_storage_cluster_join_mode is not supported by this build
  • Iceberg partition timezone is not supported by this build
  • Export merge tree partition is not supported by this build

Plus, on aarch64 only, [ Error ] in icebergS3 table function/glue catalog/* and iceberg cache/glue catalog/* — AWS Glue catalog access (external infra), not TRUNCATE. None of these are the TRUNCATE feature.

Regression — S3Export (part)/(partition), release & aarch64Export merge tree part/partition is not supported by this build; the feature is not present in this version. Same suite-vs-build version skew. Unrelated to this PR.

Regression — Swarms, release & aarch64Swarm object_storage_cluster is not supported by this build. Same version skew (note the 24 retries all passed).

Stress test (arm_release) — no crash / sanitizer / logical-error signature anywhere in the log; the failure is Code: 210. DB::NetException: Connection refused (localhost:9000) — the server didn't come up in that iteration (Failures: 2/3). Environmental/flaky. Safe to re-run.

Grype Scan (server, non-alpine) — the scan reports "Completed with 0 high/critical vulnerabilities" but the job ran 6h 0m → it timed out. The alpine/keeper variants passed. Infra timeout; this PR changes no dependencies or Dockerfiles. Safe to re-run.

PR — aggregate gate; red only because the children above are red. No independent failure.


⚠️ Coverage gap worth noting

The feature's own integration test, test_storage_iceberg_no_spark/test_iceberg_truncate.py (added/edited by this PR), was skipped — the Integration tests (amd_asan_ubsan, targeted) job resolved to skipped, and CI did detect it as a changed_integration_test. So TRUNCATE itself was not actually exercised by this run. Before merge I'd recommend making sure that targeted integration job runs (re-trigger integration tests / confirm it isn't being gated out) so the feature gets a real green signal — none of the failing checks above validate the TRUNCATE path.


Analysis is CI-evidence-based (I can't build/run ClickHouse here). Bottom line: fix the DCO sign-off; the remaining reds are pre-existing/infra/version-skew and don't block on account of this PR's code.

@zvonand
zvonand merged commit 911f0dc into antalya-26.6 Aug 12, 2026
262 of 293 checks passed
@zvonand zvonand added verified Approved for release port-antalya PRs to be ported to all new Antalya releases labels Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

antalya antalya-26.6 forwardport This is a frontport of code that existed in previous Antalya versions port-antalya PRs to be ported to all new Antalya releases releasy Created/managed by RelEasy verified Approved for release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants