Description
PR #365 was supposed to add forward compatibility to TaggedFieldSerializer, but I think the logic is flawed. It probably works if you are deserializing a single tagged class (and maybe that's how it got through testing), but since the bytes of unrecognized tagged fields are ignored, those bytes are not skipped in the input stream, so all subsequent objects in the stream will be corrupt.
Here's a test class. I wrote a small list of objects. Then I commented out a field to simulate an old version of the app trying to read future data. Only the first object in the list is not corrupt.
public static void main(String[] args) {
MockFutureClass first = new MockFutureClass(1, 2, 3);
MockFutureClass second = new MockFutureClass(4, 5, 6);
ArrayList<MockFutureClass> array = new ArrayList<>(2);
array.add(first);
array.add(second);
System.out.println(array.toString());
Kryo kryo = new Kryo();
kryo.getTaggedFieldSerializerConfig().setIgnoreUnknownTags(true);
kryo.register(MockFutureClass.class, new TaggedFieldSerializer(kryo, MockFutureClass.class));
//
// try {
// FileOutputStream fileOutputStream = new FileOutputStream(FILE_LOCATION);
// Output output = new Output(fileOutputStream);
// kryo.writeObject(output, array);
// output.close();
// } catch (FileNotFoundException e){
// e.printStackTrace();
// }
try {
Input input = new Input(new FileInputStream(FILE_LOCATION));
ArrayList<MockFutureClass> newObj = kryo.readObject(input, ArrayList.class);
System.out.println(newObj.toString());
input.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static class MockFutureClass {
@TaggedFieldSerializer.Tag(0) int x;
@TaggedFieldSerializer.Tag(1) int y;
//@TaggedFieldSerializer.Tag(2) int z;
public MockFutureClass(){};
public MockFutureClass (int x, int y, int z){
this.x = x;
this.y = y;
//this.z = z;
}
public String toString (){
return String.format("(%d, %d)", x, y);
}
// public String toString (){
// return String.format("(%d, %d, %d)", x, y, z);
// }
}
I think forward compatibility could be achieved through the use of a new annotation. Put an @Late
before an @Tag
for any fields added to new releases of an application. These fields are then written with chunked encoding so the bytes can be skipped in older releases of an application. Still an advantage over CompatibleFieldSerializer because the chunked encoding is only needed if the class evolves to use new tagged fields--otherwise buffers don't need to be allocated for chunking. And if you don't care about forward compatibility, just don't use @Late
.