Skip to content

HDDS-14561. SCMRatisRequest/ResponseProto should use the shaded protobuf from Ratis#9733

Open
Russole wants to merge 9 commits intoapache:masterfrom
Russole:HDDS-14561
Open

HDDS-14561. SCMRatisRequest/ResponseProto should use the shaded protobuf from Ratis#9733
Russole wants to merge 9 commits intoapache:masterfrom
Russole:HDDS-14561

Conversation

@Russole
Copy link
Contributor

@Russole Russole commented Feb 8, 2026

What changes were proposed in this pull request?

  • Updated SCM Ratis request/response serialization and deserialization
    to use Ratis shaded protobuf (ByteString / ByteBuffer) at the transport boundary.
  • Removed unnecessary intermediate byte[] conversions in the SCM Ratis message path.
  • Updated existing unit tests to match the revised serialization and deserialization
    implementation.

What is the link to the Apache JIRA

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

How was this patch tested?

Copy link
Contributor

@szetszwo szetszwo left a comment

Choose a reason for hiding this comment

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

@Russole , thanks for working on this!

Let see if the current change can be compiled -- I think we need to change the protobuf compiler or use shading to make SCMRatisRequest/Response extending the protobuf inside Ratis.

@Russole
Copy link
Contributor Author

Russole commented Feb 9, 2026

Thanks @szetszwo!

The current change compiles and all CI checks pass:
https://github.com/Russole/ozone/actions/runs/21776638347

Agreed that SCMRatisRequestProto and SCMRatisResponseProto still extend
com.google.protobuf.*, as they are generated by the standard protobuf compiler.
This PR focuses on the SCM↔Ratis message boundary, updating the wrapping/parsing
to use Ratis shaded protobuf types and avoid extra byte[] conversions.

Let me know if you think it makes sense to open a follow-up JIRA for generating
the SCM Ratis protos with the shaded protobuf.

Comment on lines 117 to 118
UnsafeByteOperations.unsafeWrap(
requestProto.toByteString().asReadOnlyByteBuffer()));
Copy link
Contributor

Choose a reason for hiding this comment

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

@Russole , I see how this change works now -- it does prevent copying to an array but still needs type conversion from com.google.protobuf.ByteString to org.apache.ratis.thirdparty.com.google.protobuf.ByteString.

Let's fix also the conversion in this JIRA; see ContainerCommandRequestProto as an example for how to do it.

Copy link
Contributor

Choose a reason for hiding this comment

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

unsafeWrap is not needed anymore.

    return Message.valueOf(requestProto.toByteString());

@Russole
Copy link
Contributor Author

Russole commented Feb 10, 2026

Thanks for the clarification!

I’m working on this now. I see the issue with the remaining type conversion between
com.google.protobuf and the Ratis shaded protobuf, and I’m updating the implementation
to follow the same pattern as ContainerCommandRequestProto so that the SCM Ratis
request/response fully use the shaded protobuf types.

I’ll update the PR once this is addressed.

@Russole Russole requested a review from szetszwo February 11, 2026 17:25
Copy link
Contributor

@szetszwo szetszwo left a comment

Choose a reason for hiding this comment

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

@Russole , thanks for the great work! I believe that you have spent a lot time on this.

Please see the comments inlined and also https://issues.apache.org/jira/secure/attachment/13080780/9733_review.patch

if (object instanceof ByteString) {
return (ByteString) object;
}
if (object instanceof com.google.protobuf.ByteString) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we still need to keep supporting com.google.protobuf.ByteString? Should everything used in SCM Codec have changed to org.apache.ratis.thirdparty.com.google.protobuf.ByteString after this PR?

Comment on lines 49 to 50
codecs.put(com.google.protobuf.ByteString.class, new ByteStringCodec());
codecs.put(org.apache.ratis.thirdparty.com.google.protobuf.ByteString.class,
Copy link
Contributor

Choose a reason for hiding this comment

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

If com.google.protobuf.ByteString is still needed,

  • Add a new class, say ScmByteStringCodec, for org.apache.ratis.thirdparty.com.google.protobuf.ByteString.
  • import org.apache.ratis.thirdparty.com.google.protobuf.ByteString.
    codecs.put(com.google.protobuf.ByteString.class, new ByteStringCodec());
    codecs.put(ByteString.class, new ScmByteStringCodec());

If com.google.protobuf.ByteString is not needed, just remove it.

// toByteArray returns a new array
return ProtoUtils.unsafeByteString(Ints.toByteArray(
((ProtocolMessageEnum) object).getNumber()));
return UnsafeByteOperations.unsafeWrap(Ints.toByteArray(((ProtocolMessageEnum) object).getNumber()));
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI, filed HDDS-14623 for removing ProtoUtils.


@Override
public ByteString serialize(Object object) {
return ((Message)object).toByteString();
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to support both com.google.protobuf.Message and org.apache.ratis.thirdparty.com.google.protobuf.Message?

If yes, use two GeneratedMessageCodec classes instead of using one class. Also, we need to put them to CodecFactory.

//CodecFactory
    codecs.put(com.google.protobuf.Message.class, new GeneratedMessageCodec());
    codecs.put(Message.class, new ScmGeneratedMessageCodec());

Comment on lines 47 to 48
throw new org.apache.ratis.thirdparty.com.google.protobuf.InvalidProtocolBufferException(
e.getMessage());
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's include the type in the message and use e as the cause (it will keep e's stack trace.)

      throw new InvalidProtocolBufferException("Failed to deserialize value for " + type, e);

Comment on lines 117 to 118
UnsafeByteOperations.unsafeWrap(
requestProto.toByteString().asReadOnlyByteBuffer()));
Copy link
Contributor

Choose a reason for hiding this comment

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

unsafeWrap is not needed anymore.

    return Message.valueOf(requestProto.toByteString());

Comment on lines 183 to 189
private static byte[] bytesOf(com.google.protobuf.ByteString b) {
return b.toByteArray();
}

private static byte[] bytesOf(org.apache.ratis.thirdparty.com.google.protobuf.ByteString b) {
return b.toByteArray();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Use the following assertByteStringEquals and remove the bytesOf methods.

  private static void assertByteStringEquals(com.google.protobuf.ByteString proto2, ByteString proto3) {
    assertEquals(UnsafeByteOperations.unsafeWrap(proto2.asReadOnlyByteBuffer()), proto3);
  }

Comment on lines 308 to 316
private static String shortDebugStringProto2(
com.google.protobuf.MessageOrBuilder msg) {
return com.google.protobuf.TextFormat.shortDebugString(msg);
}

private static String shortDebugStringProto3(
org.apache.ratis.thirdparty.com.google.protobuf.MessageOrBuilder msg) {
return org.apache.ratis.thirdparty.com.google.protobuf.TextFormat.shortDebugString(msg);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Use assertShortDebugString below:

  private static void assertShortDebugString(com.google.protobuf.MessageOrBuilder proto2,
      org.apache.ratis.thirdparty.com.google.protobuf.MessageOrBuilder proto3) {
    assertEquals(com.google.protobuf.TextFormat.shortDebugString(proto2),
        org.apache.ratis.thirdparty.com.google.protobuf.TextFormat.shortDebugString(proto3));
  }

when(reply.getMessage()).thenReturn(Message.valueOf(
org.apache.ratis.thirdparty.com.google.protobuf.ByteString.copyFrom(
proto.toByteArray())));
org.apache.ratis.thirdparty.com.google.protobuf.UnsafeByteOperations
Copy link
Contributor

Choose a reason for hiding this comment

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

import UnsafeByteOperations.

Message msg = Message.valueOf(
org.apache.ratis.thirdparty.com.google.protobuf.ByteString.copyFrom(
proto.toByteArray()));
org.apache.ratis.thirdparty.com.google.protobuf.UnsafeByteOperations
Copy link
Contributor

Choose a reason for hiding this comment

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

import UnsafeByteOperations.

@Russole
Copy link
Contributor Author

Russole commented Feb 12, 2026

Thanks @szetszwo for the detailed review and the patch. I really appreciate your time and guidance.
I’ll review the comments and apply the suggested changes, and will update the PR shortly.

Copy link
Contributor

@szetszwo szetszwo left a comment

Choose a reason for hiding this comment

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

@Russole , thanks for the update! Just have some minor comments inlined.

Comment on lines 35 to 36
final byte[] bytes = ((Message) object).toByteString().toByteArray();
return ByteString.copyFrom(bytes);
Copy link
Contributor

Choose a reason for hiding this comment

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

Use unsafeWarp as below?

UnsafeByteOperations.unsafeWrap(((Message) object).toByteString().asReadOnlyByteBuffer());

public Message deserialize(Class<?> type, ByteString value)
throws InvalidProtocolBufferException {
public Object deserialize(Class<?> type, ByteString value)
throws org.apache.ratis.thirdparty.com.google.protobuf.InvalidProtocolBufferException {
Copy link
Contributor

Choose a reason for hiding this comment

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

Keep throws InvalidProtocolBufferException since the import is changed.

static final Class<?>[] TYPES = {String.class, Integer.class, byte[].class};

static <T> ByteString randomValue(Class<T> clazz) {
static <T> org.apache.ratis.thirdparty.com.google.protobuf.ByteString randomValueProto3(Class<T> clazz) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ByteString is already imported.

Comment on lines 49 to 57
return org.apache.ratis.thirdparty.com.google.protobuf.ByteString.copyFromUtf8(string);
} else if (clazz == Integer.class) {
final ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.putInt(RANDOM.nextInt());
return ByteString.copyFrom(buffer.array());
return org.apache.ratis.thirdparty.com.google.protobuf.ByteString.copyFrom(buffer.array());
} else if (clazz == byte[].class) {
final byte[] bytes = new byte[RANDOM.nextInt(3)];
RANDOM.nextBytes(bytes);
return ByteString.copyFrom(bytes);
return org.apache.ratis.thirdparty.com.google.protobuf.ByteString.copyFrom(bytes);
Copy link
Contributor

Choose a reason for hiding this comment

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

ByteString is already imported.

Comment on lines 127 to 176
@@ -145,8 +145,8 @@ public void testDecodeMissingMethodNameShouldFail() throws Exception {
.build();

Message msg = Message.valueOf(
org.apache.ratis.thirdparty.com.google.protobuf.ByteString.copyFrom(
proto.toByteArray()));
org.apache.ratis.thirdparty.com.google.protobuf.UnsafeByteOperations
.unsafeWrap(proto.toByteString().asReadOnlyByteBuffer()));

InvalidProtocolBufferException ex = assertThrows(
InvalidProtocolBufferException.class,
@@ -173,8 +173,8 @@ public void testDecodeMissingArgumentTypeShouldFail() throws Exception {
.build();

Message msg = Message.valueOf(
org.apache.ratis.thirdparty.com.google.protobuf.ByteString.copyFrom(
proto.toByteArray()));
org.apache.ratis.thirdparty.com.google.protobuf.UnsafeByteOperations
Copy link
Contributor

Choose a reason for hiding this comment

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

import UnsafeByteOperations.

@Russole Russole requested a review from szetszwo February 14, 2026 00:15
Copy link
Contributor

@szetszwo szetszwo left a comment

Choose a reason for hiding this comment

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

@Russole , thanks for the update! Just one last comment inlined.

public Object deserialize(Class<?> type, ByteString value)
throws InvalidProtocolBufferException {
return value;
return com.google.protobuf.ByteString.copyFrom(value.toByteArray());
Copy link
Contributor

Choose a reason for hiding this comment

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

Use com.google.protobuf.UnsafeByteOperations.

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