Copy on Read Behavior

Methods that do a get type of operation receive as a return value a direct reference to the cached object. This provides the value as quickly as possible, but it also makes possible code implementations that could incorrectly modify the referenced object, bypassing the distribution framework and causing region entries that are no longer consistent across cluster members.

The code that has the potential for harming cache consistency by using a reference to access and change a region entry is code that executes within the servers. Examples are cache writers and listeners, transactions, and functions. A client invocation of a get type of operation that is handled by the servers is not subject to this potential for harm, as the clients are in a distinct JVM from the servers, and references do not cross JVM boundaries. That client cannot receive a return value that is a direct reference to a region entry, as the servers hold the region entries and the servers do not reside within the client JVM.

To avoid modification of the referenced object, create a copy in one of two ways:

  • Change the entry retrieval behavior for your cache by setting the copy-on-read cache attribute to true; its default value is false. When copy-on-read is true, all entry access methods return copies of the entries. This protects all server-side code from inadvertently modifying in-place. This attribute will negatively impact performance and memory consumption when a copy is not needed, as it takes time and memory to create the copy. Note that the copy-on-read attribute is applied at the cache level; it cannot be set for individual regions.

    There are two ways to set the copy-on-read attribute:

    • Set the attribute in the cache.xml file that defines the cache.

      <cache copy-on-read="true">
       ...
      </cache>
      
    • Use gfsh alter runtime to set the copy-on-read attribute once the servers have been started.

  • Implement server-side code that creates and uses a copy of the returned object. For objects that are cloneable or serializable, copy the entry value to a new object using org.apache.geode.CopyHelper.copy. Example:

    Object o = region.get(key);
    StringBuffer s = (StringBuffer) CopyHelper.copy(o);
    // further operations on the region entry value will use s
    s.toUpperCase();
    

    Always use a Region method to then change data in the region. Do not use the reference returned from the entry access method. If the upper case string should become the new value for the region entry:

    region.put(key, s);