Skip to content

HDDS-16350. Avoid a point-get for the start-key check in RDBTable.getRangeKVs - #11169

Open
rich7420 wants to merge 1 commit into
apache:masterfrom
rich7420:HDDS-16350
Open

HDDS-16350. Avoid a point-get for the start-key check in RDBTable.getRangeKVs#11169
rich7420 wants to merge 1 commit into
apache:masterfrom
rich7420:HDDS-16350

Conversation

@rich7420

@rich7420 rich7420 commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

RDBTable.getRangeKVs decided whether startKey exists with get(startKey) == null,
a full point-get that materializes and then discards the value byte[], immediately
before it.seek(startKey) lands the iterator on that same key.

seek() already positions the iterator and returns the landing entry without consuming
it (the range loop below still starts from that entry), so the existence check can reuse
the landing entry instead of a separate get: startKey is present iff the landing key
equals startKey.

Before:

if ((prefix == null || startKey.length > prefix.length)
    && get(startKey) == null) {
  // Key not found, return empty list
  return result;
}
it.seek(startKey);

After:

final KeyValue<byte[], byte[]> seeked = it.seek(startKey);
if ((prefix == null || startKey.length > prefix.length)
    && (seeked == null || !Arrays.equals(seeked.getKey(), startKey))) {
  // start key not found, return empty list
  return result;
}

This drops one point-get per getRangeKVs call plus the value byte[] it allocated only
to discard. Behavior is unchanged: the loop still starts at the seek() landing entry, and
the empty-result path fires on exactly the same inputs.

A JMH microbenchmark over a real RocksDB (100k keys, 512-byte values, present start key),
old (extra get) vs new (seek landing check), average time and allocation per call:

page time old -> new alloc old -> new
1 1.62 -> 0.99 us 1136 -> 640 B/op
10 3.86 -> 3.39 us 6176 -> 5680 B/op
100 27.6 -> 25.6 us 56576 -> 56080 B/op
1000 254 -> 250 us 560581 -> 560085 B/op

The saving is exactly one point-get: a constant ~496 B/op and ~0.5-0.6 us per call, so it is
largest as a fraction on small pages (~39% at page 1) and shrinks toward the iteration cost
as pages grow.

What is the link to the Apache Jira

https://issues.apache.org/jira/browse/HDDS-16350

How was this patch tested?

https://github.com/rich7420/ozone/actions/runs/33313659945

…RangeKVs

getRangeKVs used get(startKey) == null to decide whether an absent start key
returns an empty list, a full point-get whose value is discarded, right before
seeking to the same key. seek() already returns the landing entry without
consuming it, so compare the landing key to startKey and drop the extra
point-get; the loop still starts from startKey. Behavior unchanged, covered by
the new testRangeKVsStartKeyInclusiveAndAbsent and the existing
testPrefixedRangeKVs.

Claude-Session: https://claude.ai/code/session_01FUpCUnmy6JzHPGvwMhyGzq
Copilot AI lite review requested due to automatic review settings August 30, 2026 14:42

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chungen0126
chungen0126 self-requested a review August 31, 2026 09:50
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