Skip to content

HIVE-27190: Fix cache-key collisions for time-travel queries on Iceberg#6380

Merged
deniskuzZ merged 4 commits into
apache:masterfrom
deniskuzZ:HIVE-27190
May 26, 2026
Merged

HIVE-27190: Fix cache-key collisions for time-travel queries on Iceberg#6380
deniskuzZ merged 4 commits into
apache:masterfrom
deniskuzZ:HIVE-27190

Conversation

@deniskuzZ

@deniskuzZ deniskuzZ commented Mar 19, 2026

Copy link
Copy Markdown
Member

What changes were proposed in this pull request?

Introduced Table#getQualifier(), which extends the fully qualified name with the metatable name and the time-travel / snapshot ref when present, and uses it as the cache key.

Why are the changes needed?

Fixes a data-correctness bug: when a single query references the same table at multiple points in time - entries collide in the cache. A later reader reuses the partition list / column stats that were computed for a different snapshot, producing wrong row counts, wrong plans, and incorrect query results.

Does this PR introduce any user-facing change?

No

How was this patch tested?

mvn test -Dtest=TestIcebergCliDriver -Dqfile=iceberg_partition_pruner_cache_key.q

@sonarqubecloud

Copy link
Copy Markdown

Comment thread ql/src/java/org/apache/hadoop/hive/ql/optimizer/ppr/PartitionPruner.java Outdated
} else if (tab.isNonNative()) {
long snapshotId = tab.getStorageHandler().getSnapshotId(tab);
if (snapshotId > 0) {
key = tab.getFullyQualifiedName() + "." + snapshotId + ";";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change it here?
In my opinion,
key = tab.getFullyQualifiedName() + "." + tab.getSnapshotRef() + ";"
can also represent a unique key.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tag/branch/as of might reference the same snapshot, but ATM all of them produce diff keys, isn't it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late reply.
My understanding is that if tag/ branch point to the same snapshot, then their snapshot IDs should be the same. Am I wrong?

@deniskuzZ deniskuzZ May 5, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that is what this PR is about. we use snapshotRef as part of the key - not snapshotId. tag/branch, timetravel produce diff keys for the same snapshot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me rephrase this. If a branch (test_branch) and a tag (test_tag) are both created from main at the same time, then their snapshot IDs will be the same, and the key will be the same (key = tab.getFullyQualifiedName() + "." + snapshotId + ";").
If data is written to test_branch later, then test_branch's current snapshot ID and test_tag's snapshot ID will become different. And the keys will be the different.

Is that correct?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that is correct.

@deniskuzZ deniskuzZ May 12, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhangbutao, i've revisited the fix. Since it's query level cache, adding new API in SH is probably an overkill. created getQualifier to account for all supported cases.
WDYT?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that's fine.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for checking, @zhangbutao!

@zhangbutao zhangbutao left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 LGTM

@deniskuzZ deniskuzZ changed the title HIVE-27190: Implement col stats cache for hive iceberg table HIVE-27190: Fix cache-key collisions for time-travel and metadata-table queries on Iceberg May 13, 2026
@deniskuzZ deniskuzZ changed the title HIVE-27190: Fix cache-key collisions for time-travel and metadata-table queries on Iceberg HIVE-27190: Fix cache-key collisions for time-travel queries on Iceberg May 13, 2026
@deniskuzZ deniskuzZ requested a review from zhangbutao May 13, 2026 15:43
@zhangbutao zhangbutao requested a review from Copilot May 23, 2026 14:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a correctness issue where partition-pruning and column-stats caches can collide when the same Iceberg table is referenced at different points in time (time travel), by incorporating a time-travel/metatable qualifier into table identity and cache keys.

Changes:

  • Introduces Table#getQualifier() and uses it to extend table identity/cache keys for time-travel and Iceberg metadata-table scans.
  • Updates partition pruner and Calcite planning paths to use the new qualifier-based identity, and adjusts shared-work merge checks accordingly.
  • Adds an Iceberg CLI query test + expected outputs to validate distinct planning/stats for current vs time-travel scans.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
ql/src/java/org/apache/hadoop/hive/ql/parse/PrunedPartitionList.java Changes partition-list key API from Optional<String> to String and updates list creation.
ql/src/java/org/apache/hadoop/hive/ql/parse/ParseContext.java Updates column-stats cache lookup to use the new String key API; minor generics cleanup.
ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java Incorporates table qualifier into Calcite table identity to avoid time-travel collisions.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/SharedWorkOptimizer.java Requires matching qualifier when deciding whether two TableScans can be merged.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/ppr/PartitionPruner.java Builds partition-pruner cache key using Table#getQualifier().
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/RelOptHiveTable.java Uses String partition-list keys (and computeIfAbsent) for column-stats caching.
ql/src/java/org/apache/hadoop/hive/ql/metadata/Table.java Adds getQualifier() to represent metatable/time-travel identity for caching/planning.
iceberg/iceberg-handler/src/test/queries/positive/iceberg_partition_pruner_cache_key.q New query test covering current vs tag-based time travel in the same query/session.
iceberg/iceberg-handler/src/test/results/positive/iceberg_partition_pruner_cache_key.q.out New golden output for the added cache-key regression test.
iceberg/iceberg-handler/src/test/results/positive/iceberg_metadata_table_alias.q.out Updates expected output to reflect metadata-table qualification in optimized SQL.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ql/src/java/org/apache/hadoop/hive/ql/metadata/Table.java
Comment on lines 54 to 58
public PrunedPartitionList(Table source, String key, Set<Partition> partitions,
List<String> referred, boolean hasUnknowns) {
this.source = Objects.requireNonNull(source);
this.ppListKey = Optional.ofNullable(key);
this.ppListKey = key;
this.referred = Objects.requireNonNull(referred);
Comment thread ql/src/java/org/apache/hadoop/hive/ql/optimizer/SharedWorkOptimizer.java Outdated

@zhangbutao zhangbutao left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost looks good to me. Thanks @deniskuzZ .

Comment thread ql/src/java/org/apache/hadoop/hive/ql/metadata/Table.java
} else if (tab.isNonNative()) {
long snapshotId = tab.getStorageHandler().getSnapshotId(tab);
if (snapshotId > 0) {
key = tab.getFullyQualifiedName() + "." + snapshotId + ";";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that's fine.

@deniskuzZ

Copy link
Copy Markdown
Member Author

@zhangbutao, addresses the review comments. Please let me know if i missed anything

@sonarqubecloud

Copy link
Copy Markdown

@zhangbutao zhangbutao left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 LGTM
Thanks @deniskuzZ

@deniskuzZ deniskuzZ merged commit d2d7dd2 into apache:master May 26, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants