29 Using Continuous Query Caching
This chapter includes the following sections:
- Overview of Using Continuous Query Caching
The continuous query feature combines a query result with a continuous stream of related events to maintain an up-to-date query result in a real-time fashion. - Understanding Use Cases for Continuous Query Caching
There are several general-use categories for continuous query caching. - Understanding the Continuous Query Cache Implementation
The Coherence implementation of continuous query is found in thecom.tangosol.net.cache.ContinuousQueryCacheclass. - Constructing a Continuous Query Cache
There are two items that you must define when using continuous query caching. - Cleaning up the resources associated with a ContinuousQueryCache
If a continuous query cache is only used for a short amount of time, then an application must explicitly call therelease()method on theContinuousQueryCache. - 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. - Listening to a Continuous Query Cache
You can use synchronous and asynchronous event listeners to observe a continuous query cache. - Making a Continuous Query Cache Read-Only
TheContinuousQueryCachecan be made into a read-only cache.
Parent topic: Performing Data Grid Operations
Overview of Using Continuous Query Caching
Continuous query is implemented 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 continuous query is a cached query result that never gets out-of-date.
Parent topic: Using Continuous Query Caching
Understanding Use Cases for Continuous Query Caching
-
It is an ideal building block for Complex Event Processing (CEP) systems and event correlation engines.
-
It is ideal for situations in which an application repeats a particular query, and would benefit from always having instant access to the up-to-date result of that query.
-
A continuous query cache is analogous to a materialized view, and is useful for accessing and manipulating the results of a query using the standard
NamedCacheAPI, and receiving an ongoing stream of events related to that query. -
A continuous query cache can be used in a manner similar to a Near Cache, because it maintains an up-to-date set of data locally where it is being used, for example on a particular server node or on a client desktop; note that a Near Cache is invalidation-based, but the continuous query cache actually maintains its data in an up-to-date manner.
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 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.
Parent topic: Using Continuous Query Caching
Understanding the Continuous Query Cache Implementation
com.tangosol.net.cache.ContinuousQueryCache class.This class, like all Coherence caches, implements the standard NamedCache interface, which includes the following capabilities:
-
Cache access and manipulation using the
Mapinterface:NamedCacheextends the standardMapinterface from the Java Collections Framework, which is the same interface implemented by the JavaHashMapandHashtableclasses. -
Events for all objects modifications that occur within the cache:
NamedCacheextends theObservableMapinterface. -
Identity-based clusterwide locking of objects in the cache:
NamedCacheextends theConcurrentMapinterface. -
Querying the objects in the cache:
NamedCacheextends theQueryMapinterface. -
Distributed Parallel Processing and Aggregation of objects in the cache:
NamedCacheextends theInvocableMapinterface.
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.
Parent topic: Using Continuous Query Caching
Constructing a Continuous Query Cache
-
The underlying cache that the continuous query cache is based on
-
A query of the 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 instance, 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");
The query is the same type of query that would be used to filter data. For example:
Filter filter = new AndFilter(new EqualsFilter("getTrader", traderid),
new EqualsFilter("getStatus", Status.OPEN));
Normally, to query a cache, a method from QueryMap is used. For example, to obtain a snap-shot of all open trades for a trader object:
Set setOpenTrades = cache.entrySet(filter);
Similarly, the continuous query cache is constructed from those same two pieces:
ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter);
Parent topic: Using Continuous Query Caching
Cleaning up the resources associated with a ContinuousQueryCache
release() method on the ContinuousQueryCache.Parent topic: Using Continuous Query Caching
Caching only keys, or caching both keys and values
CacheValues property to be configured; for example:
ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter, false);
If necessary, the CacheValues property can also be modified after the cache has been instantiated. For example:
cacheOpenTrades.setCacheValues(true);
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. If the continuous query cache has a non-null transformer, it is
inferred that cache values should be stored locally, then the
CacheValues property is automatically set to
true.
Parent topic: Using Continuous Query Caching
Listening to a Continuous Query Cache
This section includes the following topics:
- Overview of Listening to a Continuous Query Cache
- Achieving a Stable Materialized View
- Support for Synchronous and Asynchronous Listeners
Parent topic: Using Continuous Query Caching
Overview of Listening to a Continuous Query Cache
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:
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:
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:
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:
ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter, listener);
Parent topic: Listening to a Continuous Query Cache
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 Using Map Events.
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.
Parent topic: Listening to a Continuous Query Cache
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 Using Continuous Query Caching.
Parent topic: Listening to a Continuous Query Cache
Making a Continuous Query Cache Read-Only
ContinuousQueryCache can be made into a read-only cache. For example:
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.
Parent topic: Using Continuous Query Caching