Back to JOC 10.1.2 Tutorial Index

Distributed Cache

The Cache Service is designed to allow object updates and invalidations to be propagated between caches within the system. For simplicity, the default for both the cache and cache objects is to not distribute changes between caches. To take advantage of this feature, a small bit of configuration for both the cache administrator and the cache user is required. To change the default for a cache object, the cache user must set the object attribute Attributes.DISTRIBUTE (see the chapter Cache Object Attributes) for the object. To change the default for the cache, the cache administrator must initialize the cache as distributed (see the chapter Cache Administration). A distributed cache may contain both local and distributed objects, while all objects in a cache configured to be local will be treated as local objects. That is, if the cache is configured as local the DISTRIBUTE attribute of the object is ignored.

In the distributed cache, object changes (invalidate, destroy and replace) are propagated through the cache's messaging system which is built on top of TCP. This messaging system requires a known address/port to allow a cache to "join" the cache system when it is first initialized. This address can be set in a .properties file and loaded with the Cache.open() method, or set explicitly with the Cache.init(CacheAttributes) method (see Cache Administration for details). If an address is not supplied, a default of localhost, port 12345 is used. It is best to specify your own address and port to avoid conflicts with other software. If the cache system is running over multiple machines, multiple addresses can be configured. This will avoid any dependency on a particular machine being available or on the startup order of the processes. It is important that all caches cooperating in the same cache system specify the same set of addresses. This list of addresses is used to define the caches that make up a particular cache system. If the address lists vary, the cache system could be partitioned into distinct groups resulting in inconsistencies between caches.

import oracle.ias.cache.*;
import java.net.InetAddress;
import java.net.UnknownHostException;

  CacheAttributes attr = new CacheAttributes();
  attr.setDistribute(true); // mark the cache as distributed

  try
  {

    attr.addCacheAddr(InetAddress.getLocalHost(), 6666); // add a cache address

  }
  catch (UnKnownHostException ex)
  {

    // handle exception

  }

  try
  {

    Cache.init(attr);

  }
  catch (CacheException ex)
  {

    // handle exception

  }
When updating, invalidating or destroying an object across multiple caches it is often useful to know when the action has completed at all the participating sites. This is possible by setting the REPLY attribute on the object. This will cause all caches to send a reply to the sender when the requested action has completed. The application can wait for the action to complete by using the CacheAccess.waitForResponse() method. If the responses are going to be ignored CacheAccess.cancelResponse() should be called to free the cache resources used to collect the responses. waitForResponse and cancelResponse both apply to all objects accessed by the CacheAccess object. This allows the application to update a number of objects then wait for all the replies at once. The following code illustrates how to set an object for distributed update/invalidate and how to handle the reply. In this example the attributes are set for the entire region. They could also be set for a group or an individual object, whichever is appropriate for the application.
import oracle.ias.cache.*;

  CacheAccess cacc;
  String obj;
  Attributes attr = new Attributes();
  MyLoader loader = new MyLoader();

  // mark the object for distribution and have a reply generated by the remote caches
  // when the change is completed
  attr.setFlags(Attributes.DISTRIBUTE | Attributes.REPLY);
  attr.setLoader(loader);

  CacheAccess.defineRegion("testRegion",attr); // create region with distributed attributes

  cacc = CacheAccess.getAccess("testRegion");

  obj = (String)cacc.get("testObject");

  cacc.replace("testObject", obj + "new version"); // change will be propagated to other caches

  cacc.invalidate("invalidObject"); // invalidation is propagated to other caches

  try
  {

    // wait for up to a second (1000 milliseconds) for both the update and the
    // invalidate to complete
    cacc.waitForResponse(1000);

  }
  catch (TimeoutException ex)
  {

    // tired of waiting so cancel the response
    cacc.cancelResponse();

  }

  cacc.close();
In some cases it may be necessary to coordinate the loading or updating of an object across all the caches in the system. This can be done by defining an object or group of objects as Attributes.SYNCHRONIZE. When an object or group is defined as SYNCHRONIZE, an application must obtain "ownership" before the object (or an object within the group) can be loaded or replaced. Ownership is obtained with the CacheAccess.getOwnership() method. Once ownership has been obtained, no other CacheAccess instance will be allowed to load or replace the object. Reads and invalidates of objects are not affected by synchronization. Once ownership has been obtained and the modification to the object (or objects in the case of a group) have been completed, CacheAccess.releaseOwnership() must be called. releaseOwnership will wait up to the specified time for the update(s) to complete at the remote caches. If the update(s) complete within that time ownership is released, otherwise a TimeoutException is thrown. If the method times out releaseOwnership should be called again. releaseOwnership must return successfully for the ownership to be released. If the time out value is -1, ownership will be released immediately without waiting for the responses from the other caches.
import oracle.ias.cache.*;

  CacheAccess cacc;
  String obj;
  Attributes attr = new Attributes();
  MyLoader loader = new MyLoader();

  // mark the object for distribution and have a reply generated by the remote caches
  // when the change is completed
  attr.setFlags(Attributes.DISTRIBUTE | Attributes.SYNCHRONIZE);
  attr.setLoader(loader);

  CacheAccess.defineRegion("testRegion"); // create region

  cacc = CacheAccess.getAccess("testRegion");

  attr.setFlags(Attributes.DISTRIBUTE | Attributes.SYNCHRONIZE);

  cacc.defineGroup("syncGroup", attr);   // define a distributed synchronized group
  cacc.defineObject("syncObject", attr); // define a distributed synchronized object

  attr.setFlagsToDefaults() // reset attribute flags

  // define a group where SYNCHRONIZE is the default for all objects in the group
  attr.setFlags(Attributes.DISTRIBUTE | Attributes.SYNCHRONIZE_DEFAULT);
  cacc.defineGroup("syncGroup2", attr);

  try
  {

    // try to get the ownership for the group
    // don't wait more than 5 seconds
    cacc.getOwnership("syncGroup", 5000);
    obj = (String)cacc.get("testObject", "syncGroup"); // get latest object

    // replace the object with a new version
    cacc.replace("testObject", "syncGroup", obj + "new version");

    obj = (String)cacc.get("testObject2", "syncGroup"); // get a second object

    // replace the object with a new version
    cacc.replace("testObject2", "syncGroup", obj + "new version");

  }
  catch (TimeoutException ex)
  {

    System.out.println("unable to acquire ownership for group");
    cacc.close();
    return;

  }

  try
  {

    cacc.releaseOwnership("syncGroup", 5000);

  }
  catch (TimeoutException ex)
  {

    // tired of waiting so just release ownership
    cacc.releaseOwnership("syncGroup", -1));

  }

  try
  {

    // try to get the ownership for the object
    // don't wait more than 5 seconds
    cacc.getOwnership("syncObject", 5000);
    obj = (String)cacc.get("syncObject"); // get latest object

    // replace the object with a new version
    cacc.replace("syncObject", obj + "new version");

  }
  catch (TimeoutException ex)
  {

    System.out.println("unable to acquire ownership for object");
    cacc.close();
    return;

  }

  try
  {

    cacc.releaseOwnership("syncObject", 5000);

  }
  catch (TimeoutException ex)
  {

    // tired of waiting so just release ownership
    cacc.releaseOwnership("syncObject", -1));

  }

  try
  {

    // try to get the ownership for the object
    // where the ownership is defined as the default for the group
    // don't wait more than 5 seconds
    cacc.getOwnership("Object2", "syncGroup2", 5000);
    obj = (String)cacc.get("Object2", "syncGroup2"); // get latest object

    // replace the object with a new version
    cacc.replace("Object2", "syncGroup2", obj + "new version");

  }
  catch (TimeoutException ex)
  {

    System.out.println("unable to acquire ownership for object");
    cacc.close();
    return;

  }

  try
  {

    cacc.releaseOwnership("Object2", 5000);

  }
  catch (TimeoutException ex)
  {

    // tired of waiting so just release ownership
    cacc.releaseOwnership("Object2", -1));

  }

  cacc.close();
Cached Object Consistency

Within the cache system, each cache manages its own objects locally. It is therefore likely that a copy of an object may exist in more than one cache. The level of consistency required between these copies will vary depending on the application and the objects being cached. It is possible to achieve various levels of consistency, from none to all copies always in sync.

The consistency between objects in different caches is categorized into 4 levels: No consistency requirements, propagate changes but don't wait for the change to complete, propagate changes and wait for the change to complete and serialize changes.

The level of consistency achieved is largely dependent on the attributes defined for the object.

  • If there are no consistency requirements the object can be defined as "local" (this is the default) in which case all updates and invalidates are only visible to the local cache.
  • To distribute changes the object should be defined with Attributes.DISTRIBUTE set. All modifications to the object will be broadcast to other caches in the system. There is no control over how or when an object is loaded into the cache or updated and there is no notification as to when the modification is complete in all caches.
  • To obtain the third level of consistency, both the Attributes.DISTRIBUTE and the Attributes.REPLY flags should be set. With the REPLY flag set, a reply is sent back to the modifying cache when the modification has been completed at a remote site. These replies are returned asynchronously, i.e., the CacheAccess.replace() and CacheAccess.invalidate() methods do not block. The CacheAccess.waitForResponse() method should be used by the application to wait for the replies.
  • For complete consistency. the Attributes.DISTRIBUTE and Attributes.SYNCHRONIZE flags should be set and the CacheLoader.load() method should do a CacheLoader.netSearch() before loading the object from an external source. Defining an object to be SYNCHRONIZE will force an application to get ownership of the object before loading or modifying it. This effectively serializes write access to the object from the cache's point of view (it does not synchronize the object from Java's point of view). Notification is also sent back to the owner when the update is complete. The CacheAccess.releaseOwnership() method will block until the replies are received. By calling netSearch in the load method, the cache will search all other caches for a copy of the object. This will prevent different versions of the object from being loaded into the cache from an external source.
To allow the user more control, the invalidate and destroy methods in both Cache and CacheAccess have been overloaded to include a boolean parameter localOnly. When localOnly is true, the invalidate and destroy operations are only limited to local cache, even if the object is distributed.

Back to JOC 10.1.2 Tutorial Index

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy