Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

There should be one serialization framework for communication and discovery protocol. 

  1. MessageWriter, MessageReader logic is depends on a remote IgniteProductVersion.
  2. Message#writeTo, Message#readFrom is auto-generated and stored separately from Message classes.
  3. Add Message#writeObject - for serializing Objects (java functions, and user objects):
    1. MessageWriter for communication protocol use BinaryMarshaller
    2. MessageWriter for discovery protocol use JdkMarshaller
  4. Message fields contain:
    1. primitive classes.
    2. known collections.
    3. POJO - that are other Message.
    4. Users classes and java functions (e.g. ComputeJob).
    5. byte[] fields (objects serialized externally) must be avoided as much as possible, because their compatibility can't be guaranteed.
  5. Order of fields in Message is fixed with @Order annotation.
  6. Add  Adding and removing fields must be go along with @Since, @Until  annotations  annotations for Message classes and fields.

...

  1. Ignite CI notifies for IF-clauses with condition based on IgniteVersion older than (curVer - 1).
  2. Ignite CI forbids code changes if Message DTO fields changed without corresponding @Since, @Until annotations.
  3. Ignite CI forbids code changes if Message DTO contains byte[] fields.
  4. Ignite CI forbids code changes if Message field changes type.
  5. Ignite CI checks @Order annotation of fields - starts with 0, no lags.

...

  1. Message#writeTo consumes MessageWriter that stores IgniteProductVersion of destination a receiver node and use it for serializing data for this version (mostly, for ignoring some fields).
  2. Message#readFrom consumes MessageReader that stores IgniteProductVersion of source node and use it for deserializing data (mostly, for setting default values of new fields).

    Code Block
    languagejava
    titleMessage
    public interface MessageWriter {
         public IgniteProductVersion destinationVersionreceiverVersion();
    }
    
    public interface MessageReader {
         public IgniteProductVersion sourceVersionsenderVersion();
    }


  3. Introduce annotations @Since and @Until for Message classes and Message fields, to use it for generating code for Message#writeTo and Message#readFrom:

Code Block
languagejava
titleMyMessage
// Package where the Message is defined.
package org.apache.ignite.internal.my.message;

// package private class.
@Since(version = "2.19.0")
public class MyMessage implements Message {
    @Order(0)
    private int id;
    
 	/** Remove field. */
    @Until(version = "2.20.0")
	    @Order(1)
     private String rmFld;

    /** New field. */
    @Since(version = "2.20.0")
 	@Order(2)
    private String newFld;

	// Message must have setters/getters for all @Ordered fields. Methods names are equal to a corresponding field name. 
	public void id(int id) {
		this.id = id;
	}

  	public int id() {
		return id;
	}

  	public void newFld(String newFld) {
		this.newFld = newFld;
	}
 
 	public String newFld() {
		return newFld;
	}

    // Delegates #writeTo method call to generated serializer.
    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
        return MyMessageSerializer.writeTo(this, buf, writer);
    }

 	// Delegates #readFrom method call to generated serializer.
    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
         return MyMessageSerializer.readFrom(this, buf, reader);
    }
}

// Generated code from the message ^ for Ignite version 2.20.0.
// Use the same package as corresponding message.
package org.apache.ignite.internal.my.message;

class MyMessageMyMessageSerializer {
    public static boolean writeTo(MyMessage msg, MByteBuffer buf, MessageWriter writer) {
        IgniteProductVersion destVerrcvVer = writer.version();

        if (destVer.lessThan(2, 19, 0))
            throw new IgniteException("Must not send the message to destination node"receiverVersion();

        writer.writeString(msg.id());

        if (destVerrcvVer.lessThan(2, 20, 0))
            writer.writeString(msg.rmFld());

        if (destVerrcvVer.greaterThanEqual(2, 20, 0))
            writer.writeString(msg.newFld());
    
        return true;
    }

    public static boolean readFrom(MyMessage msg, ByteBuffer buf, MessageReader reader) {
        IgniteProductVersion srcVer = reader.versionsenderVersion();

        msg.id(reader.readString());

		if (srcVer.lessThan(2, 20, 0))
			msg.rmFld(reader.readString());

        if (srcVer.greaterThanEqual(2, 20, 0))
            msg.newFld(reader.readString());

        return true;
    }
}

...

  1. Do not remove Message class or Message fields, but annotate it with @Until
  2. Do not change types or @Order of fields.
  3. New fields must be annotated with @Since. All such fields must be optional, default value is null. Handling the nulls is care of Message consumer on the reader side.
  4. Setters and getters must follow name of the field.

Removing annotated entities messages is allowed after current version is greater than (@Until + 1) or (@Since + 1).

Transport compatibility 

...