[Question] Deserialization error when registering multiple classes in XLANG mode with non-Java Peer Language
Question
Environment:
- Fury version: 0.10.0
- Mode: XLANG
- Language: Java
- Peer Language: Go
Description:
In Fury 0.10.0, when using XLANG mode, an issue occurs during deserialization when a Fury instance registers more than one class, and the peer language is not Java. Specifically, the error happens when attempting to deserialize a SomeClass3 object serialized by Fury in Go. The error message thrown by FuryJava is as follows:
Exception in thread "main" org.apache.fury.exception.ClassNotCompatibleException: Hash 528 is not consistent with 16369 for class null
at org.apache.fury.serializer.StructSerializer.xread(StructSerializer.java:173)
at org.apache.fury.Fury.xreadNonRef(Fury.java:1046)
at org.apache.fury.Fury.xreadRef(Fury.java:982)
at org.apache.fury.Fury.xdeserializeInternal(Fury.java:854)
at org.apache.fury.Fury.deserialize(Fury.java:792)
at org.apache.fury.Fury.deserialize(Fury.java:718)
at org.apache.fury.Main.main(Main.java:225)
However, when only registering SomeClass3, the deserialization works correctly without any issues.
Steps to Reproduce:
- Register both classes in FuryGo:
fury.register(SomeClass3.class, "SomeClass3"); fury.register(SomeClass2.class, "SomeClass2"); - Serialize an instance of
SomeClass3using FuryGo. - Attempt to deserialize the serialized data in FuryJava.
- Observe the error mentioned above.
Analysis:
Upon investigation, I found the following code snippet in ClassResolver::addSerializer(Class<?> type, Serializer<?> serializer):
short typeId = serializer.getXtypeId();
if (typeId != Fury.NOT_SUPPORT_CROSS_LANGUAGE) {
if (typeId > Fury.NOT_SUPPORT_CROSS_LANGUAGE) {
typeIdToClassXLangMap.put(typeId, type);
}
if (typeId == Fury.FURY_TYPE_TAG_ID) {
typeTag = serializer.getCrossLanguageTypeTag();
typeTagToClassXLangMap.put(typeTag, type);
}
}
For custom classes like SomeClass3 and SomeClass2, the serializer is always a StructSerializer. However, the getXtypeId() function in StructSerializer always returns Fury.FURY_TYPE_TAG_ID, as seen below:
@Override
public short getXtypeId() {
return Fury.FURY_TYPE_TAG_ID;
}
This causes the mapping in typeIdToClassXLangMap.put(typeId, type) to be overwritten each time, since both classes use the same typeId. As a result, during deserialization, the following line in Fury::xreadNonRef(MemoryBuffer buffer, Serializer<?> serializer) retrieves the last mapping for Fury.FURY_TYPE_TAG_ID, which leads to an incorrect class being returned (in my case, it was SomeClass2 instead of SomeClass3):
cls = classResolver.getClassByTypeId((short) -typeId);
Question:
This behavior seems to suggest that a single Fury instance in XLANG mode can only handle one custom class. Is this behavior by design, or is this a bug?
The getXtypeId has been removed in latest main branch. In the version you tested, the deserialization will read class as a tagged class, and read tag first, then read class from tag.