Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(java): Disallow writing meta classdef when obj is null #1686

Merged
merged 9 commits into from
Aug 5, 2024

Conversation

LiangliangSui
Copy link
Contributor

What does this PR do?

When obj is null, if shareMeta is enabled, no need to write meta classdef

Related issues

Does this PR introduce any user-facing change?

  • Does this PR introduce any public API change?
  • Does this PR introduce any binary protocol compatibility change?

Benchmark

ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
classResolver.writeClass(buffer, classInfo);
writeData(buffer, classInfo, obj);
}
if (shareMeta) {
if (shareMeta && !isNull) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The deserialization will still read class meta, but the offset will be -1, then the read will fail. Could you add an UT for such case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

CI failure is not related to the current changes, fix it in #1691.

@LiangliangSui LiangliangSui force-pushed the chore_1 branch 2 times, most recently from 2f45408 to 8f4f13a Compare June 18, 2024 07:02
@chaokunyang
Copy link
Collaborator

The code looks good to me. But do we really to optimize this? When value is null, the ClassDef list is empty, only one byte are written in such cases

@LiangliangSui
Copy link
Contributor Author

The code looks good to me. But do we really to optimize this? When value is null, the ClassDef list is empty, only one byte are written in such cases

There will be two more write overheads (putInt32, writeVarUint32Small7), which is also a small problem I found while reading the code. If there is no negative impact, I think it can be merged.

@LiangliangSui
Copy link
Contributor Author

Hi @chaokunyang , could you reply to this PR?

buffer.putInt32(startOffset, buffer.writerIndex());
classResolver.writeClassDefs(buffer);
}
}

private void xserializeInternal(MemoryBuffer buffer, Object obj) {
int startOffset = buffer.writerIndex();
buffer.writeInt32(-1); // preserve 4-byte for nativeObjects start offsets.
buffer.writeInt32(
Constants.INVALID_OFFSITE); // preserve 4-byte for nativeObjects start offsets.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is an internal, could we keep it inside this class

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@chaokunyang
Copy link
Collaborator

How about checking meta list size and remove this part of code from class resolver into Fury

@LiangliangSui
Copy link
Contributor Author

I don't think it's necessary. Reading classDefOffset and reading numClassDefs should be what readClassDefs should do. We shouldn't distribute what readClassDefs should do to other functions.

Why would we do this? Just to move Constants.INVALID_OFFSITE to Fury?

@chaokunyang
Copy link
Collaborator

chaokunyang commented Jun 26, 2024

I don't think it's necessary. Reading classDefOffset and reading numClassDefs should be what readClassDefs should do. We shouldn't distribute what readClassDefs should do to other functions.

Why would we do this? Just to move Constants.INVALID_OFFSITE to Fury?

When object is not null, but the type is internal types such pritimitive type, string or ArrayList/HashMap, the meta list will be empty too, which can be optimized as the null value

@LiangliangSui LiangliangSui reopened this Jun 26, 2024
@LiangliangSui
Copy link
Contributor Author

but the type is internal types such pritimitive type

Not only internal types, but also the meta list of registered classes(classInfo.classId != NO_CLASS_ID) is empty.

How about checking meta list size and remove this part of code from class resolver into Fury

  private void write(MemoryBuffer buffer, Object obj) {
    int startOffset = buffer.writerIndex();
    boolean shareMeta = config.isMetaShareEnabled();
    if (shareMeta) {
      buffer.writeInt32(INVALID_OFFSITE); // preserve 4-byte for nativeObjects start offsets.
    }
    // reduce caller stack
    boolean isNull = refResolver.writeRefOrNull(buffer, obj);
    if (!isNull) {
      ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
      classResolver.writeClass(buffer, classInfo);
      writeData(buffer, classInfo, obj);
    }
    // **Just determine the meta list size.**
    if (shareMeta && !isNull && !serializationContext.getMetaContext().writingClassDefs.isEmpty()) {
      buffer.putInt32(startOffset, buffer.writerIndex());
      classResolver.writeClassDefs(buffer);
    }
  }

Is it possible to determine the meta list size at the end?

@LiangliangSui
Copy link
Contributor Author

but the type is internal types such pritimitive type

Not only internal types, but also the meta list of registered classes(classInfo.classId != NO_CLASS_ID) is empty.

How about checking meta list size and remove this part of code from class resolver into Fury

  private void write(MemoryBuffer buffer, Object obj) {
    int startOffset = buffer.writerIndex();
    boolean shareMeta = config.isMetaShareEnabled();
    if (shareMeta) {
      buffer.writeInt32(INVALID_OFFSITE); // preserve 4-byte for nativeObjects start offsets.
    }
    // reduce caller stack
    boolean isNull = refResolver.writeRefOrNull(buffer, obj);
    if (!isNull) {
      ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
      classResolver.writeClass(buffer, classInfo);
      writeData(buffer, classInfo, obj);
    }
    // **Just determine the meta list size.**
    if (shareMeta && !isNull && !serializationContext.getMetaContext().writingClassDefs.isEmpty()) {
      buffer.putInt32(startOffset, buffer.writerIndex());
      classResolver.writeClassDefs(buffer);
    }
  }

Is it possible to determine the meta list size at the end?

@chaokunyang Any other suggestions? If so, please let me know.

@chaokunyang
Copy link
Collaborator

the meta list of registered classes(classInfo.classId != NO_CLASS_ID) is empty.

For the meta list of registered classes(classInfo.classId != NO_CLASS_ID), we still need to write meta, this needs being done before we set scopde meta share as the default compatible mode

@chaokunyang
Copy link
Collaborator

We can check meta size before writing meta

@LiangliangSui
Copy link
Contributor Author

the meta list of registered classes(classInfo.classId != NO_CLASS_ID) is empty.

For the meta list of registered classes(classInfo.classId != NO_CLASS_ID), we still need to write meta, this needs being done before we set scopde meta share as the default compatible mode

I mean if the current class has been registered, the information of the current class will not be written back to meta.

public void writeClass(MemoryBuffer buffer, ClassInfo classInfo) {
  if (classInfo.classId == NO_CLASS_ID) { // no class id provided.
    // use classname
    if (metaContextShareEnabled) {
      buffer.writeByte(USE_CLASS_VALUE_FLAG);
      // FIXME(chaokunyang) Register class but not register serializer can't be used with
      //  meta share mode, because no class def are sent to peer.
      writeClassWithMetaShare(buffer, classInfo);
    } else {
      // if it's null, it's a bug.
      assert classInfo.packageNameBytes != null;
      metaStringResolver.writeMetaStringBytesWithFlag(buffer, classInfo.packageNameBytes);
      assert classInfo.classNameBytes != null;
      metaStringResolver.writeMetaStringBytes(buffer, classInfo.classNameBytes);
    }
  } else {
    // use classId
    buffer.writeVarUint32(classInfo.classId << 1);
  }
}

@LiangliangSui
Copy link
Contributor Author

We can check meta size before writing meta

Done

package org.apache.fury.type;

public class Constants {
public static final int INVALID_OFFSITE = -1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I perfer not expose this variable. It's an internal implementation, we'd better keep it private

Signed-off-by: LiangliangSui <[email protected]>
Signed-off-by: LiangliangSui <[email protected]>
Signed-off-by: LiangliangSui <[email protected]>
@LiangliangSui
Copy link
Contributor Author

Hi @chaokunyang , could you please take a look?

Signed-off-by: LiangliangSui <[email protected]>
@LiangliangSui
Copy link
Contributor Author

The above comments has been completed, please help review again.

@LiangliangSui
Copy link
Contributor Author

@chaokunyang please help review this

Copy link
Collaborator

@chaokunyang chaokunyang left a comment

Choose a reason for hiding this comment

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

LGTM

@chaokunyang chaokunyang merged commit 6aa7686 into apache:main Aug 5, 2024
32 checks passed
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