How to modify / evolve a distributed Hot Infinispan cache without losing entries?

5

Context

I have a cluster with some JBoss EAP 6.4 nodes. Applications on cluster nodes share a cache of Infinispan in embedded mode with synchronous distribution via UDP (JGroups):

<cache-container name="meu-container"
        default-cache="meu-cache"
        jndi-name="java:jboss/infinispan/cache/meu-container">
   <transport stack="udp"/>
   <distributed-cache name="meu-cache" 
         owners="2"  
         mode="SYNC" />
   <!-- Outros caches -->       
</cache-container>  

This cache uses a String as a key and a POJO as a value:

@Resource(lookup="java:jboss/infinispan/cache/meu-container/meu-cache")
private Cache<String, MeuPojo> meuCache;

MeuPojo is a simple object Serializable :

public class MeuPojo implements java.io.Serializable {

    private static final long serialVersionUID = 4L;  

    private String campoUm;
    private Integer campoDois;

    // Outros campos, getters, setters, etc

} 

The distributed cache works perfectly and all instances of meuCache "see" all entries. Changes in any instance of MeuPojo are seen in all nodes, new entries or deletions of entries on any node are "reflected" on other nodes.

During a upgrade of the application without updates in MeuPojo the cluster nodes go up and down one by one without prejudice to the cache . As soon as a node goes up it gains access to its copy of the cache entries.

The problem

The problem happens when I modify MeuPojo . In this case Infinispan does not behave very well due to differences in serialization. If I add a new field (eg private String campoTres; ) the updated nodes can not see the entries with non-updated versions of MeuPojo in meuCache and vice versa (we do not see updated versions of MeuPojo ) .

Nothing significant happens in logs independent of the value of serialVersionUID . Which leads me to believe that Infinispan is swallowing exceptions like InvalidClassException .

Currently every time we want to add a new field in MeuPojo is a struggle. The workaround is to use two caches ( meu-cache and meu-cache-v2 ) and two different classes MeuPojo and MeuPojoV2 . We copy the entries manually into the application and we change which cache is "valid" for the applications in each upgrade .

I think there must be a less "dumb" way to evolve MeuPojo without losing cache entries, but I could not find a solution. Ideally, upgraded viewers should see entries in non-updated versions of MeuPojo (as if they had campo3 = null ) and vice versa.

Has anyone been through this and knows a solution or at least a less labor-intensive work-around?

    
asked by anonymous 24.08.2016 / 22:39

1 answer

2

I see at least two possible alternatives, but there must be more. The first one is the simplest, but also the most fragile, and consists of freezing the set of fields of the existing classes (as well as freezing serialVersionUID ) and forcing new fields to be added with the transient modifier. Any violation of this rule will cause the problem you describe.

The other alternative is to take control of serialization yourself by implementing the following methods:

private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
private void readObjectNoData() throws ObjectStreamException;

These methods are described in the interface documentation Serializable . Basically, they override the standard form of serialization and allow you to define your own particular way of performing the serialization. The writeObject method is responsible for serializing the object and readObject for deserializing. The readObjectNoData method decides what to do when the object to be created comes from a corrupted stream .

The ObjectOutputStream is the object responsible for serializing the object and it knows the object being serialized. That way, if you use the methods ObjectOutputStream.defaultWriteObject() and ObjectInputStream.defaultReadObject() , the non- transient of the object in question will be serialized and deserialized, respectively, in the standard way that java does. You can then use these methods to write and read the default fields and use the other methods in the to write and read the values of the other fields that will be marked as transient . Or you do not use these methods and add / get the values of the object fields manually when using the other stream methods.

In addition, you may also prefer to implement the Externalizable to have better control over the stream .

    
24.08.2016 / 23:10