Skip to content

TaggedFieldSerializer not actually forward compatible #442

Closed
@cypherdare

Description

@cypherdare

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions