Repository navigation
Conversation
…ty/ClickHouse into data_lake_clustering_read
…9.2-with-panic-safe-ffi) Co-authored-by: Cursor <cursoragent@cursor.com>
…9.2-with-panic-safe-ffi)
…ty/ClickHouse into data_lake_clustering_read
|
This PR is dependent on changes to delta-kernel-rs |
Audit Report: Altinity/ClickHouse PR #2395PR: Altinity/ClickHouse#2395 — Read clustering columns from delta lake and use it for data skipping AI audit note: This review comment was generated by AI (claude-opus-4.6). ScopeThis PR adds support for reading Delta Lake liquid clustering columns from domain metadata ( Files changed
Call GraphTransition Matrix
Confirmed DefectsMedium: Clustering columns not invalidated on credential-rotation snapshot rebuild
Code evidence ( // Line 760: kernel_snapshot_state is reset, but clustering_columns is NOT
kernel_snapshot_state.reset();
initOrUpdateSnapshot();
snapshot_stats = getSnapshotStatsImpl();
// clustering_columns still holds old value hereCode evidence ( void TableSnapshot::initOrUpdateClusteringColumns() const
{
if (clustering_columns.has_value()) // <-- returns early with stale data
return;
// ...
}Medium:
|
…ty/ClickHouse into data_lake_clustering_read
|
AI audit note: This review comment was generated by AI (claude-4.6-opus). Audit update for PR Altinity/ClickHouse#2395 (Delta Lake liquid-clustering predicate fallback): Scope
Call GraphConfirmed defects:Low: Trivial
Coverage summary:
Additional observations (not defects):
|
|
TESTING |
|
|
||
| addSettingsChanges(settings_changes_history, "26.6", | ||
| { | ||
| {"delta_lake_clustering_predicate_fallback", false, true, "New setting to enable clustering-aware predicate fallback for Delta Lake tables with Liquid Clustering."}, |
There was a problem hiding this comment.
Must be only in Antalya section above.
| return schema->physical_names_map; | ||
| } | ||
|
|
||
| const DB::Names & TableSnapshot::getClusteringColumns() const |
There was a problem hiding this comment.
I did not find where is this method called. Is it used, or just dead code?
There was a problem hiding this comment.
yes this function is not used, removed.
|
|
||
| if (!result) | ||
| { | ||
| LOG_TRACE(log, "No delta.clustering domain metadata found — table is not liquid-clustered"); |
There was a problem hiding this comment.
I think make sense to add a table name in message.
There was a problem hiding this comment.
TableSnapshot does not store the table name, but added the table location. IKernelHelper::getTableLocation()
| logical_columns = std::move(physical_columns); | ||
| } | ||
|
|
||
| LOG_TRACE(log, "Clustering columns: {}", fmt::join(logical_columns, ", ")); |
There was a problem hiding this comment.
Make sense to add table name here too.
There was a problem hiding this comment.
Same here added IKernelHelper::getTableLocation()
|
|
||
| auto arr = obj->getArray("clusteringColumns"); | ||
| if (!arr) | ||
| throw DB::Exception(DB::ErrorCodes::BAD_ARGUMENTS, "delta.clustering: missing clusteringColumns array"); |
There was a problem hiding this comment.
I thing BAD_ARGUMENTS is incorrect code here. It is used for arguments in queries, when here is some metadata incosistency.
There was a problem hiding this comment.
Replaced with INCORRECT_DATA error code.
| { | ||
| throw DB::Exception( | ||
| DB::ErrorCodes::BAD_ARGUMENTS, | ||
| "Failed to parse delta.clustering domain metadata: {}", e.displayText()); |
There was a problem hiding this comment.
The same as above about error code.
And what is going on when this exception thrown? Does customer have an opinion to execute query in old way?
There was a problem hiding this comment.
Replaced with INCORRECT_DATA error code.
| } | ||
|
|
||
| void TableSnapshot::initOrUpdateClusteringColumns() const | ||
| { |
There was a problem hiding this comment.
As I understand this method called in any case, but clustering_columns is used only when delta_lake_clustering_predicate_fallback=1. Make sense to check this setting here to avoid useless snapshot parsing when feature is turned off.
There was a problem hiding this comment.
Added logic to run initOrUpdateClusteringColumns only if delta_lake_clustering_predicate_fallback is set.
const bool need_clustering_columns = filter_dag
&& settings[DB::Setting::delta_lake_enable_engine_predicate]
&& settings[DB::Setting::delta_lake_clustering_predicate_fallback];
if (need_clustering_columns)
initOrUpdateClusteringColumns();
…ty/ClickHouse into data_lake_clustering_read
|
@ianton-ru can u please check this, i have addressed all comments. |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c9dbbc8ab0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| auto result = KernelUtils::unwrapResult( | ||
| ffi::get_domain_metadata_internal( | ||
| state->snapshot.get(), | ||
| KernelUtils::toDeltaString("delta.clustering"), | ||
| state->engine.get(), | ||
| KernelUtils::allocateString), | ||
| "get_domain_metadata_internal(delta.clustering)"); |
There was a problem hiding this comment.
Retry clustering metadata reads after credential rotation
When a cached snapshot uses catalog-vended temporary credentials that refresh only after an ExpiredToken, this new domain-metadata read runs with the stale engine and lets the resulting DELTA_KERNEL_ERROR escape. Because it happens before constructing Iterator, the existing scan retry cannot call refreshCredentials; consequently, the first filtered query after token expiry fails even though ordinary scans and stats recover. Apply the same refresh/rebuild/one-shot retry used by the other snapshot operations around this FFI call.
Useful? React with 👍 / 👎.
| # Tolerate pull failures for images that are expected to be present | ||
| # locally (e.g. CLICKHOUSE_CI_MIN_TESTED_VERSION tags that only exist | ||
| # in a private registry); `docker compose up` will use the local image. | ||
| images_pull_cmd = self.base_cmd + ["pull", "--ignore-pull-failures"] |
There was a problem hiding this comment.
Keep pull failures visible to the retry wrapper
For any ordinary registry failure, this flag suppresses the failure that run_and_check must observe to trigger its three retries. Docker documents --ignore-pull-failures as pulling what it can while ignoring failed images (Docker CLI reference); therefore a flaky pull can silently leave a stale same-tag image in place and run integration tests against it, or defer a missing-image failure until compose up without the intended pull retries. Limit the exception to the known local-only image rather than applying it to every service image.
AGENTS.md reference: AGENTS.md:L153-L153
Useful? React with 👍 / 👎.
| { | ||
| {"object_storage_cluster_fallback_to_local_if_empty", false, false, "New setting"}, | ||
| {"use_puffin_files_cache", false, true, "Enables cache of parsed Puffin file content such as deletion vectors."}, | ||
| {"delta_lake_clustering_predicate_fallback", false, true, "New setting to enable clustering-aware predicate fallback for Delta Lake tables with Liquid Clustering."}, |
There was a problem hiding this comment.
Record the setting at its actual release version
This commit is based on 26.6.8.20002, but the newly introduced setting is recorded as having changed in 26.6.2.20001. As a result, a user selecting compatibility with any intervening release such as 26.6.7 will not have the setting reverted to false, even though that release predates the feature. Move this history entry to the version that actually ships the new default so compatibility profiles reproduce the requested release behavior.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
moved to 26.6.8.20002 section
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8453b94bc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /* schema */nullptr), | ||
| "scan"); | ||
|
|
||
| if (engine_predicate_exception && enable_clustering_predicate_fallback && !clustering_columns.empty()) |
There was a problem hiding this comment.
Trigger fallback for unsupported predicate nodes
For the intended a = 42 AND cityHash64(b) != 0 case, unsupported functions return VISITOR_FAILED_OR_UNSUPPORTED from EngineIterator::getNextImpl without setting engine_predicate_exception; only thrown exceptions populate that pointer. Consequently this condition is false, the clustering fallback and its profile event never run, and the newly added fallback test observes zero hits. Detect unsupported translation explicitly rather than using only the exception pointer.
Useful? React with 👍 / 👎.
| # Full scan — note the total scanned files. | ||
| full_scan_id = f"{TABLE_NAME}_full_scan" | ||
| instance.query(f"SELECT count() FROM {TABLE_NAME}", query_id=full_scan_id) |
There was a problem hiding this comment.
Force the baseline query to scan files
With delta-kernel enabled, an unfiltered SELECT count() uses totalRows() and the trivial-count optimization, whose stats visitor does not increment DeltaLakeScannedFiles. Therefore full_scan_files is zero while the filtered point query scans at least one file, so the later point_scan_files < full_scan_files assertion fails instead of testing pruning. Disable optimize_trivial_count_query for this baseline or read an actual column.
Useful? React with 👍 / 👎.
CI triage for
|
CI triage for #2395Summary: none of the failures look caused by this PR. I checked 7 failing suites plus DCO. All the failures are either already on There's no CI result yet for the current head The PR only changes DeltaLake (
Evidence
Next steps
Triage based on the praktika S3 reports for this PR, #2713 and #2655. Nothing was re-run locally. |
CI triage for
|
| Check | Failure | Verdict |
|---|---|---|
| Regression release settings | /settings/default values/delta_lake_clustering_predicate_fallback |
🔴 PR-caused (new setting has no snapshot) |
| Regression release settings | iceberg_min_data_file_size_bytes, iceberg_target_data_file_size_bytes |
⚪ pre-existing (settings that come from the base branch) |
| Regression release ssl_server_3 | /ssl server/part 3/zookeeper/secure connection |
⚪ pre-existing / base-branch change |
| Integration tests (amd_asan_ubsan, targeted), 19/34 | iceberg test_minmax_pruning ×12, test_writes_statistics_by_minmax_pruning ×6, test_remote_initiator_global_join_without_analyzer[s3] |
⚪ pre-existing |
| Stateless (amd_asan_ubsan, distributed plan, parallel, 2/2) | 05387_s3_cluster_remote_initiator_keeps_stripped_join |
⚪ pre-existing |
| Stateless (amd_asan_ubsan, distributed plan, parallel, 2/2) | 01661_extract_all_groups_throw_fast |
⚪ flaky (0/3 reruns failed) |
| Stateless (amd_asan_ubsan, distributed plan, parallel, 1/2) | Logical error: GroupingAggregatedTransform pushed bucket A twice → Server died |
⚪ pre-existing |
🔴 Regression release settings: delta_lake_clustering_predicate_fallback
/settings/default values/delta_lake_clustering_predicate_fallback Fail
AssertionError ... SnapshotNotFoundError(
The settings suite compares the default value of every setting with a stored snapshot. This PR adds DECLARE(Bool, delta_lake_clustering_predicate_fallback, true, ...) in src/Core/Settings.cpp, and the snapshot has no entry for it yet. This is expected for any new setting, and the setting code itself is fine.
Fix: add a snapshot entry for delta_lake_clustering_predicate_fallback (default 1) to the settings suite in Altinity/clickhouse-regression, or have the regression team regenerate the snapshots for 26.6. No change is needed in this PR.
The other two failures in the same suite (iceberg_min_data_file_size_bytes, iceberg_target_data_file_size_bytes) are also SnapshotNotFoundError. Those settings come from the base branch (they sit in the 26.6.8.20003.altinityantalya block of SettingsChangesHistory.cpp), not from this PR. The same snapshot update should cover them.
⚪ Regression release ssl_server_3: zookeeper secure connection
Code: 999. DB::Exception: All connection tries failed while connecting to ZooKeeper. nodes: secure://172.18.0.3:2281
Poco::Exception. Code: 1000, e.code() = 0, Certificate validation error: Unacceptable certificate from 172.18.0.3: application verification failure
This is a TLS certificate validation failure on the ClickHouse→Keeper connection. This PR only touches DeltaLake code. The likely cause is #2669 ("Verify the server certificate hostname on outbound TLS connections"), which was merged into antalya-26.6 on 2026-10-25. CI runs on the PR merge ref, so that change is in this build. Here the node is reached by IP (172.18.0.3), so the certificate is now probably rejected by the hostname/IP check. In #2669's own CI the SSLServer job was skipping, so this suite has likely not run since the change landed. I couldn't confirm the cause from a base-branch run, though.
Next step: not for this PR. The ssl_server regression's zookeeper certificates (SAN) or config probably need updating after #2669.
⚪ Integration tests (amd_asan_ubsan, targeted): iceberg minmax pruning + remote initiator
test_minmax_pruning.py:116 E assert 0 == 3 (pruned files)
test_writes_statistics_by_minmax_pruning.py:89 E assert 0 == 3
test_remote_initiator_global_join_without_analyzer[s3]: Code: 206 ... No alias for subquery or table function in JOIN
These are the same 19 tests, failing the same way, in the old-analyzer integration shards of unrelated backport PRs #2698 and #2685. Nothing here goes through the DeltaLake code this PR changes. The PR's test_storage_delta changes made the "targeted" job run these shards, which is why they show up here. Safe to ignore for this PR.
⚪ Stateless: 05387_s3_cluster_remote_initiator_keeps_stripped_join
The test fails only with the randomized --prefer_localhost_replica 0 (S3_ERROR ... HTTP response code: 403) and passes 35/35 without randomization. The same test fails in the same suite on #2685. It's pre-existing and not related to DeltaLake.
⚪ Stateless: 01661_extract_all_groups_throw_fast
The test expected TOO_LARGE_ARRAY_SIZE (128) and got code 241 (MEMORY_LIMIT_EXCEEDED) under ASan, then failed 0 of 3 reruns. That is a timing/memory flake, and the query doesn't touch any code this PR changes.
⚪ Stateless 1/2: GroupingAggregatedTransform pushed bucket 4 twice (server died)
This is a logical error in MergingAggregatedMemoryEfficientTransform.cpp:63, which is in aggregation and has nothing to do with DeltaLake. The same STID 3994-4c1d also fires on #2698 (Stateless tests (amd_asan_ubsan, cas s3 storage, parallel, 1/2)). It's a known pre-existing crash on the base branch.
Bottom line: none of the failures point to a bug in the DeltaLake clustering code. The only PR-caused item is the missing settings snapshot for the new setting, and that is fixed in clickhouse-regression, not here. I didn't push any changes to this branch.
|
@ianton-ru can u please check |
closes: ##2394
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):
Read clustering columns from
delta-kernel-rsand use that for data skipping.CI/CD Options
Exclude tests:
Regression jobs to run: