Repository navigation
Fix: partition index bucket overflow - #9977
Merged
JingsongLi merged 4 commits intoSep 20, 2026
Merged
JingsongLi merged 4 commits into
JingsongLi merged 4 commits into
Conversation
Reject bucket counts outside the short-backed index range and validate bucket ids before narrowing while allowing bucket id 32767 in bounded and unbounded assignment. Signed-off-by: Akash Reddy Jammula <akashreddyjammula@gmail.com>
Cover the 32768 bucket-count boundary, stable assignment of bucket id 32767, invalid configured limits, and out-of-range restored bucket ids. Signed-off-by: Akash Reddy Jammula <akashreddyjammula@gmail.com>
Enforce the shared 32768-bucket limit, support bucket id 32767, and reject incompatible bucket ids during index restoration. Signed-off-by: Akash Reddy Jammula <akashreddyjammula@gmail.com>
Clarify that dynamic-bucket.max-buckets accepts -1 or a fixed value from 1 through 32768. Signed-off-by: Akash Reddy Jammula <akashreddyjammula@gmail.com>
JingsongLi
reviewed
Sep 20, 2026
JingsongLi
left a comment
Contributor
There was a problem hiding this comment.
Requirement fit: SUPPORTED (triage: GO)
Implementation: CLEAN
The 32,768 cap matches the representable non-negative short bucket-id range, and the patch validates both configured values and restored metadata in Java and Python. Boundary tests cover 32,768 as valid and 32,769 as invalid. I did not find an actionable implementation issue.
The current red Flink 2 job is SinkSavepointITCase.testRecoverFromSavepoint timing out after 180 seconds; that test and path are unrelated to this PR, while the core, Python, documentation, and other integration jobs pass. Please rerun that job before merge.
XiaoHongbo-Hope
pushed a commit
that referenced
this pull request
Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pr is on #9276
Purpose
PartitionIndexstores the mapping from a key hash to its assigned bucket in anInt2ShortHashMap:Bucket IDs are non-negative, so this representation supports IDs from 0 through 32767 , or at most 32768 buckets.
However, dynamic-bucket.max-buckets was an unrestricted integer. When it was configured above 32768 , PartitionIndex could allocate bucket ID 32768 or higher and then narrow it to a signed short .
For example, bucket ID 32768 becomes -32768 after narrowing:
First assignment: 32768
Stored short value: -32768
Later assignment: -32768
As a result, the first record for a key and subsequent records for the same key could be sent to different buckets, violating the requirement that a key always retains its bucket assignment.
The same limit also applies to PyPaimon because Python-written dynamic-bucket metadata must remain readable and writable by Java.
Before and after
Before
dynamic-bucket.max-buckets = 40000
assign(hash) -> 32768
store mapping -> (short) 32768 == -32768
assign(same hash again) -> -32768
A single key could therefore be written to two different buckets.
The unbounded path also stopped at bucket ID 32766 , even though ID 32767 is representable by the memo.
After
dynamic-bucket.max-buckets = 40000
writer initialization / assignment -> clear IllegalArgumentException or ValueError
dynamic-bucket.max-buckets = 32768
highest valid bucket ID -> 32767
repeated assignment -> 32767
All bucket IDs are checked before entering the short-backed map, so an out-of-range ID can no longer wrap into a negative bucket.
Changes
Java
PyPaimon
Documentation
Notes To Reviewer: The main design choice is to retain the compact short-backed hash-to-bucket memo and reject unsupported configurations rather than widening the value type for every dynamic-bucket table.
Validation is intentionally performed on writer/assigner initialization rather than general table schema loading. This prevents new corruption while preserving read access to existing tables that have an out-of-range option but have not necessarily created an out-of-range bucket.
The maximum is a bucket count, not a maximum bucket ID:
maximum bucket count = 32768
valid bucket IDs = 0..32767