Skip Headers
Oracle® Coherence Developer's Guide
Release 3.7

Part Number E18677-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

23 Using Continuous Query Caching

While it is possible to obtain a point in time query result from a Coherence cache to, and it is possible to receive events that would change the result of that query, Coherence provides a feature that combines a query result with a continuous stream of related events to maintain an up-to-date query result in a real-time fashion. This capability is called Continuous Query, because it has the same effect as if the desired query had zero latency and the query were being executed several times every millisecond! For more information on point in time query results and events, see Chapter 22, "Querying Data In a Cache."

Coherence implements the Continuous Query functionality by materializing the results of the query into a Continuous Query Cache, and then keeping that cache up-to-date in real-time using event listeners on the query. In other words, a Coherence Continuous Query is a cached query result that never gets out-of-date.

The following sections are included in this chapter:

23.1 Uses of Continuous Query Caching

There are several different general use categories for Continuous Query Caching:

An example use case is a trading system desktop, in which a trader's open orders and all related information must always be maintained in an up-to-date manner. By combining the Coherence*Extend functionality with Continuous Query Caching, an application can support literally tens of thousands of concurrent users.

Note:

Continuous Query Caches are useful in almost every type of application, including both client-based and server-based applications, because they provide the ability to very easily and efficiently maintain an up-to-date local copy of a specified sub-set of a much larger and potentially distributed cached data set.

23.2 The Coherence Continuous Query Cache

The Coherence implementation of Continuous Query is found in the com.tangosol.net.cache.ContinuousQueryCache class. This class, like all Coherence caches, implements the standard NamedCache interface, which includes the following capabilities:

Since the ContinuousQueryCache implements the NamedCache interface, which is the same API provided by all Coherence caches, it is extremely simple to use, and it can be easily substituted for another cache when its functionality is called for.

23.3 Constructing a Continuous Query Cache

There are two items that define a Continuous Query Cache:

  1. The underlying cache that it is based on;

  2. A query of that underlying cache that produces the sub-set that the Continuous Query Cache caches.

The underlying cache is any Coherence cache, including another Continuous Query Cache. A cache is usually obtained from a CacheFactory, which allows the developer to simply specify the name of the cache and have it automatically configured based on the application's cache configuration information; for example:

NamedCache cache = CacheFactory.getCache("orders");

See Appendix B, "Cache Configuration Elements" for more information on specifying cache configuration information.

The query is the same type of query that would be used to; for example:

Example 23-1 A Query for a Continuous Query Cache

Filter filter = new AndFilter(new EqualsFilter("getTrader", traderid),
                              new EqualsFilter("getStatus", Status.OPEN));

See Chapter 22, "Querying Data In a Cache" for more information on queries.

Normally, to query a cache, a method from QueryMap is used; for examples, to obtain a snap-shot of all open trades for this trader:

Example 23-2 Getting Data for the Continuous Query Cache

Set setOpenTrades = cache.entrySet(filter);

Similarly, the Continuous Query Cache is constructed from those same two pieces:

Example 23-3 Constructing the Continuous Query Cache

ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter);

23.3.1 Cleaning up the resources associated with a ContinuousQueryCache

A Continuous Query Cache places one or more event listeners on its underlying cache. If the Continuous Query Cache is used for the duration of the application, then the resources are cleaned up when the node is shut down or otherwise stops. However, if the Continuous Query Cache is only used for a period, then when the application is done using it, the application must call the release() method on the ContinuousQueryCache.

23.4 Caching only keys, or caching both keys and values

When constructing a Continuous Query Cache, it is possible to specify that the cache should only keep track of the keys that result from the query, and obtain the values from the underlying cache only when they are asked for. This feature may be useful for creating a Continuous Query Cache that represents a very large query result set, or if the values are never or rarely requested. To specify that only the keys should be cached, use the constructor that allows the CacheValues property to be configured; for example:

Example 23-4 A Constructor that Allows the CacheValues Property

ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter, false);

If necessary, the CacheValues property can also be modified after the cache has been instantiated; for example:

Example 23-5 Setting the CacheValues Property

cacheOpenTrades.setCacheValues(true);

23.4.1 CacheValues Property and Event Listeners

If the Continuous Query Cache has any standard (non-lite) event listeners, or if any of the event listeners are filtered, then the CacheValues property is automatically set to true, because the Continuous Query Cache uses the locally cached values to filter events and to supply the old and new values for the events that it raises.

23.5 Listening to the ContinuousQueryCache

Since the Continuous Query Cache is itself observable, it is possible for the client to place one or more event listeners onto it. For example:

Example 23-6 Adding a Listener to a Continuous Query Cache

ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter);
cacheOpenTrades.addMapListener(listener);

Assuming some processing has to occur against every item that is in the cache and every item added to the cache, there are two approaches. First, the processing could occur then a listener could be added to handle any later additions:

Example 23-7 Processing Continuous Query Cache Entries and Adding a Listener

ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter);
for (Iterator iter = cacheOpenTrades.entrySet().iterator(); iter.hasNext(); )
    {
    Map.Entry entry = (Map.Entry) iter.next();
    // .. process the cache entry
    }
cacheOpenTrades.addMapListener(listener);

However, that code is incorrect because it allows events that occur in the split second after the iteration and before the listener is added to be missed! The alternative is to add a listener first, so no events are missed, and then do the processing:

Example 23-8 Adding a Listener Before Processing Continuous Query Cache Entries

ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter);
cacheOpenTrades.addMapListener(listener);
for (Iterator iter = cacheOpenTrades.entrySet().iterator(); iter.hasNext(); )
    {
    Map.Entry entry = (Map.Entry) iter.next();
    // .. process the cache entry
    }

However, the same entry can appear in both an event an in the Iterator, and the events can be asynchronous, so the sequence of operations cannot be guaranteed.

The solution is to provide the listener during construction, and it receives one event for each item that is in the Continuous Query Cache, whether it was there to begin with (because it was in the query) or if it got added during or after the construction of the cache:

Example 23-9 Providing a Listener When Constructing the Continuous Query Cache

ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter, listener);

23.5.1 Achieving a Stable Materialized View

The ContinuousQueryCache implementation faced the same challenge: How to assemble an exact point-in-time snapshot of an underlying cache while receiving a stream of modification events from that same cache. The solution has several parts. First, Coherence supports an option for synchronous events, which provides a set of ordering guarantees. See Chapter 21, "Using Cache Events," for more information on this option.

Secondly, the ContinuousQueryCache has a two-phase implementation of its initial population that allows it to first query the underlying cache and then subsequently resolve all of the events that came in during the first phase. Since achieving these guarantees of data visibility without any missing or repeated events is fairly complex, the ContinuousQueryCache allows a developer to pass a listener during construction, thus avoiding exposing these same complexities to the application developer.

23.5.2 Support for Synchronous and Asynchronous Listeners

By default, listeners to the ContinuousQueryCache have their events delivered asynchronously. However, the ContinuousQueryCache does respect the option for synchronous events as provided by the SynchronousListener interface. See Chapter 23, "Using Continuous Query Caching," for more information on this option.

23.6 Making the ContinuousQueryCache Read-Only

The ContinuousQueryCache can be made into a read-only cache; for example:

Example 23-10 Making the Continuous Query Cache Read-Only

cacheOpenTrades.setReadOnly(true);

A read-only ContinuousQueryCache does not allow objects to be added to, changed in, removed from or locked in the cache.

When a ContinuousQueryCache has been set to read-only, it cannot be changed back to read/write.