Skip to content

HDDS-14238. Move RDBBatchOperation Byte comparison to native comparison for optimization - #9550

Merged
swamirishi merged 2 commits into
apache:masterfrom
swamirishi:HDDS-14238
Dec 29, 2025
Merged

HDDS-14238. Move RDBBatchOperation Byte comparison to native comparison for optimization#9550
swamirishi merged 2 commits into
apache:masterfrom
swamirishi:HDDS-14238

Conversation

@swamirishi

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Instead of performing ByteWise comparison on Java side moving the comparison to native side would more optimal since most of the transactions are going to use direct byte buffers. Here we intend to use the rocksdb Slice to perform bytewise comparisons. RocksDB Bytewise comparators also uses the same comparator.
https://github.com/facebook/rocksdb/blob/c110091d368b8a01b5be36a14198769e60786c05/util/comparator.cc#L36-L38
Unfortunately currently in 7.7.3 there is no direct native comparison that has been implemented. We can look into contributing to Rocksdb by having a JNI implementation for

https://github.com/facebook/rocksdb/blob/0bf9079d44eea91afda7151306d3a3439a39511b/java/src/main/java/org/rocksdb/NativeComparatorWrapper.java#L16

Till then this is a nice workaround to have.

What is the link to the Apache JIRA

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

How was this patch tested?

Update existing unit tests

@swamirishi
swamirishiforce-pushed the HDDS-14238 branch 2 times, most recently from e4e5016 to 692b5edCompareDecember 24, 2025 13:51
@swamirishi
swamirishiforce-pushed the HDDS-14238 branch 8 times, most recently from a3349d7 to deb7900CompareDecember 25, 2025 05:47
…on for optimization
Change-Id: Ia7655ff5148197be488a2c1151ec7fd1d6f9d452
@swamirishi

Copy link
Copy Markdown
ContributorAuthor

@szetszwo This is good to be reviewed. I will click on ready for review when I get a +1. I don't want to run the CI multiple times.

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

@swamirishi , thanks for working on this! Please see the comments inlined.

Comment on lines 103 to 111
private void initWithByteArray(byte[] array) {
this.slice = new ManagedSlice(array);
this.hash = ByteBuffer.wrap(array).hashCode();
}

ByteBuffer asReadOnlyByteBuffer() {
return buffer.asReadOnlyByteBuffer();
private void initWithDirectByteBuffer(ByteBuffer byteBuffer) {
this.slice = new ManagedDirectSlice(byteBuffer);
this.hash = byteBuffer.hashCode();
}

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.

Constructors should not call any functions. Otherwise, the fields cannot be final, which is very useful for reducing bugs and for understanding the code..

privatefinalAbstractSlice<?> slice;
/** Cache the hash value. */privatefinalinthash;
staticBytesnewBytes(CodecBufferbuffer) {
Objects.requireNonNull(buffer, "buffer == null");
returnbuffer.isDirect() ? newBytes(buffer.asReadOnlyByteBuffer()) : newBytes(buffer.getArray());
}
Bytes(ByteBufferbuffer) {
Objects.requireNonNull(buffer, "buffer == null");
assertTrue(buffer.isDirect(), "buffer is not direct");
this.slice = newManagedDirectSlice(buffer);
this.hash = buffer.hashCode();
}
Bytes(byte[] array) {
Objects.requireNonNull(array, "array == null");
this.slice = newManagedSlice(array);
this.hash = ByteBuffer.wrap(array).hashCode();
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

public String toString() {
return array != null ? bytes2String(array)
: bytes2String(asReadOnlyByteBuffer());
return slice.toString();

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.

Have you checked what will it return? Could you show an example output?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

public static void main(String[] args) throws CodecException {
String value = "test";
Bytes bytes = new Bytes(value.getBytes(UTF_8));
System.out.println("To String Heap Value: " + bytes);
Bytes dbytes = newBytes(CodecBufferCodec.get(true).fromPersistedFormat(value.getBytes(UTF_8)));
System.out.println("To String Direct Value: " + dbytes);
}
2025-12-28 18:25:34,786 [main] INFO db.CodecBuffer (CodecBuffer.java:set(85)) - Successfully set constructor to LeakDetector::newCodecBuffer: org.apache.hadoop.hdds.utils.db.CodecBuffer$$Lambda$4/1694556038@7085bdee
To String Heap Value: test
To String Direct Value: test

@szetszwoszetszwoDec 29, 2025

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.

To String Heap Value: test
To String Direct Value: test

Isn't the data supposed to be binary? This seems not working.

@swamirishiswamirishiDec 29, 2025

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

When you say it is not working. Maybe you are not loading the rocksdb libs before this. I forgot there was static codeblock loading the rocksdb lib.
This should work:

public static void main(String[] args) throws CodecException {
ManagedRocksObjectUtils.loadRocksDBLibrary();
String value = "test";
Bytes bytes = new Bytes(value.getBytes(UTF_8));
System.out.println("To String Heap Value: " + bytes);
Bytes dbytes = newBytes(CodecBufferCodec.get(true).fromPersistedFormat(value.getBytes(UTF_8)));
System.out.println("To String Direct Value: " + dbytes);
}

@swamirishiswamirishiDec 29, 2025

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The output before the change and after will be the same since StringUtils.byte2String() also decodes the byte value to String value

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

If we want the binary value then we have to print the hex value.
slice.toString(true) would do that

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I did the above to keep the behaviour same as before.

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.

You are right -- it is the same as before.

assertEquals(fromArray.hashCode(), fromBuffer.hashCode());
assertEquals(fromArray, fromBuffer);
assertEquals(fromBuffer, fromArray);
for (CodecBuffer.Allocator allocator : ImmutableList.of(CodecBuffer.Allocator.HEAP, CodecBuffer.Allocator.DIRECT)) {

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.

Pass allocator instead of adding a loop.

runTestBytes(original, codec, CodecBuffer.Allocator.HEAP);
runTestBytes(original, codec, CodecBuffer.Allocator.DIRECT);
}
static <T> voidrunTestBytes(Tobject, Codec<T> codec, CodecBuffer.Allocatorallocator) throwsIOException {
finalbyte[] array = codec.toPersistedFormat(object);
finalBytesfromArray = newBytes(array);
try (CodecBufferbuffer = codec.toCodecBuffer(object, allocator)) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

done

@szetszwo

Copy link
Copy Markdown
Contributor

... I will click on ready for review when I get a +1. I don't want to run the CI multiple times.

@swamirishi , we should pass the CI first before asking for reviewing -- if the change cannot pass CI, it may be fundamentally incorrect. What is the point for reviewing?

⚠️ Also, reviewer's time/effort is much more important than saving the machines' time/effort.

Change-Id: I913be571b87e318abc798c7396ca072de23b01e8
@swamirishi

swamirishi commented Dec 28, 2025

Copy link
Copy Markdown
ContributorAuthor

... I will click on ready for review when I get a +1. I don't want to run the CI multiple times.

@swamirishi , we should pass the CI first before asking for reviewing -- if the change cannot pass CI, it may be fundamentally incorrect. What is the point for reviewing?

⚠️ Also, reviewer's time/effort is much more important than saving the machines' time/effort.

One can always check the CI run on the fork. We have been following this model on almost all PRs for a while.
@adoroszlai can also add his opinions on this.

@szetszwo

Copy link
Copy Markdown
Contributor

One can always check the CI run on the fork. ...

Please provide the link then. I would like to save my time for finding it.

@swamirishi

Copy link
Copy Markdown
ContributorAuthor

One can always check the CI run on the fork. ...

Please provide the link then. I would like to save my time for finding it.

https://github.com/swamirishi/ozone/actions/runs/20561058088

@swamirishi
swamirishi marked this pull request as ready for review December 29, 2025 09:32
@swamirishi

swamirishi commented Dec 29, 2025

Copy link
Copy Markdown
ContributorAuthor

@szetszwo i have addressed all your review comments is this good to be merged?

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

@swamirishi , thanks for the update! Please see the comment inlined.

public String toString() {
return array != null ? bytes2String(array)
: bytes2String(asReadOnlyByteBuffer());
return slice.toString();

@szetszwoszetszwoDec 29, 2025

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.

To String Heap Value: test
To String Direct Value: test

Isn't the data supposed to be binary? This seems not working.

@szetszwoszetszwo 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 the change looks good.

@swamirishi

Copy link
Copy Markdown
ContributorAuthor

Thank you @szetszwo for reviewing the patch

@swamirishi
swamirishi merged commit 64bb019 into apache:masterDec 29, 2025
95 of 96 checks passed
Sign up for freeto 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

@swamirishi@szetszwo