Skip to content

Ensure TreeMapMetaStore range reads return copies#5027

Open
iting0321 wants to merge 3 commits into
apache:mainfrom
iting0321:fix-treemap-readrange-copy-records
Open

Ensure TreeMapMetaStore range reads return copies#5027
iting0321 wants to merge 3 commits into
apache:mainfrom
iting0321:fix-treemap-readrange-copy-records

Conversation

@iting0321

Copy link
Copy Markdown
Contributor

Summary

Implement the TODO TODO: return a copy of each object to avoid mutating the records
Fix TreeMapMetaStore range reads so they return copied records instead of references to the records stored in the underlying slice.

Previously, readRange returned a new list but reused the stored record objects inside that list. Callers could mutate those returned records and accidentally modify the in-memory store outside a write transaction.

This change makes readRange copy each returned value with the slice’s existing copyRecord function for both:

  • empty-prefix reads
  • non-empty prefix range reads

Checklist

  • 🛡️ Don't disclose security issues! (contact security@apache.org)
  • 🔗 Clearly explained why the changes are needed, or linked related issues: Fixes #
  • 🧪 Added/updated tests with good coverage, or manually tested (and explained how)
  • 💡 Added comments for complex logic
  • 🧾 Updated CHANGELOG.md (if needed)
  • 📚 Updated documentation in site/content/in-dev/unreleased (if needed)

Copilot AI review requested due to automatic review settings July 10, 2026 06:18
@github-project-automation github-project-automation Bot moved this to PRs In Progress in Basic Kanban Board Jul 10, 2026

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

Fixes TreeMapMetaStore range reads to return defensive copies of stored records (via the slice’s existing copyRecord function) so callers can’t mutate the in-memory store by modifying objects returned from readRange.

Changes:

  • Update Slice.readRange to copy each returned value for both empty-prefix and prefix-bounded range reads.
  • Add a copyValues(...) helper to centralize the per-record copying logic.
  • Add unit tests covering empty-prefix and non-empty-prefix readRange copy behavior using PolarisGrantRecord.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
polaris-core/src/main/java/org/apache/polaris/core/persistence/transactional/TreeMapMetaStore.java Make readRange return per-record copies using copyRecord (via new copyValues).
polaris-core/src/test/java/org/apache/polaris/core/persistence/transactional/TreeMapMetaStoreTest.java Add tests verifying readRange returns copies for empty and non-empty prefixes.

Comment on lines +95 to +99
private List<T> copyValues(Collection<T> values) {
List<T> copied = new ArrayList<>(values.size());
for (T value : values) {
copied.add(this.copyRecord.apply(value));
}

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.

@iting0321 : WDYT?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I updated deleteRange to avoid calling readRange now that readRange defensively copies returned records.
I also saw the dev discussion about eventually deprecating/removing TreeMapMetaStore. So I kept this change narrow.
Thanks you!

@dimas-b dimas-b 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.

Thanks for your contribution, @iting0321 !

Changes LGTM, but please see https://lists.apache.org/thread/nzoljc1ohnsq4f5o28dh4opqkqw3p09h

Comment on lines +95 to +99
private List<T> copyValues(Collection<T> values) {
List<T> copied = new ArrayList<>(values.size());
for (T value : values) {
copied.add(this.copyRecord.apply(value));
}

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.

@iting0321 : WDYT?

dimas-b
dimas-b previously approved these changes Jul 13, 2026
@github-project-automation github-project-automation Bot moved this from PRs In Progress to Ready to merge in Basic Kanban Board Jul 13, 2026
@dimas-b dimas-b requested review from dennishuo and flyrain July 13, 2026 16:23
flyrain
flyrain previously approved these changes Jul 13, 2026

@flyrain flyrain 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.

LGTM. Thanks @iting0321 !


String endKey =
prefix.substring(0, prefix.length() - 1)
+ (char) (prefix.charAt(prefix.length() - 1) + 1);

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.

nit: The endKey prefix-successor computation is now duplicated verbatim between readRange (lines 87-89) and here. Worth pulling into a small private helper (e.g. rangeEndKey(prefix), or a shared subMap accessor both paths call) so the two range computations can't drift.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for your feedback! I just fixed it!

@iting0321 iting0321 dismissed stale reviews from flyrain and dimas-b via 8a12db1 July 15, 2026 05:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants