Skip to content

fix(transform): floor nanosecond timestamps before bucket hashing - #2891

Open
fallintoplace wants to merge 1 commit into
apache:mainfrom
fallintoplace:fix-bucket-negative-nanos
Open

fix(transform): floor nanosecond timestamps before bucket hashing#2891
fallintoplace wants to merge 1 commit into
apache:mainfrom
fallintoplace:fix-bucket-negative-nanos

Conversation

@fallintoplace

@fallintoplace fallintoplace commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

N/A. Found while comparing the Rust bucket transform with the Java reference implementation.

What changes are included in this PR?

Rust integer division truncates toward zero, while Iceberg Java converts nanoseconds to microseconds with floor division before bucket hashing. For negative values that are not exactly microsecond-aligned, dividing by 1,000 therefore hashed the wrong microsecond. For example, -1,001 ns became -1 µs instead of -2 µs, which could place pre-epoch values in a different partition bucket across implementations.

This PR centralizes nanosecond-to-microsecond conversion with Euclidean division and uses it for:

  • Arrow Time64 values with nanosecond precision
  • Arrow Timestamp values with nanosecond precision
  • timestamp_ns and timestamptz_ns literals

Positive and exactly microsecond-aligned values are unchanged.

Are these changes tested?

Yes. Unit tests assert the conversion directly for positive, aligned-negative, and unaligned-negative values, then verify that pre-epoch nanosecond literals and Arrow arrays bucket identically to their equivalent microsecond values.

Validated with:

  • cargo test -p iceberg transform::bucket::test --lib
  • cargo clippy -p iceberg --lib -- -D warnings

@fallintoplace fallintoplace changed the title fix(transform): floor pre-epoch nanosecond bucket values fix(transform): use floor division for nanosecond bucket values Jul 25, 2026
@fallintoplace
fallintoplace force-pushed the fix-bucket-negative-nanos branch from ac681e5 to cb83680 Compare July 25, 2026 15:59
@fallintoplace fallintoplace changed the title fix(transform): use floor division for nanosecond bucket values fix(transform): floor nanosecond timestamps before bucket hashing Jul 25, 2026

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

Checked this against Java implementation and it does do the same. Thanks for the PR!

}

#[inline]
fn nanos_to_micros(v: i64) -> i64 {

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.

fn test_nanos_to_micros_uses_floor_division() {
assert_eq!(Bucket::nanos_to_micros(1_001), 1);
assert_eq!(Bucket::nanos_to_micros(1_000), 1);
assert_eq!(Bucket::nanos_to_micros(-1_000), -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.

Maybe worth a sanity check for 0, i64::Min and i64::Max

@fallintoplace
fallintoplace force-pushed the fix-bucket-negative-nanos branch from cb83680 to 24e6ccd Compare July 27, 2026 08:37
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.

2 participants