13 Performing Continuous Queries (C++)
This chapter includes the following sections:
- Overview of Performing Continuous Queries (C++)
Queries provide the ability to obtain a point in time query result from a Coherence cache and it is possible to receive events that would change the result of that query. - Understanding the Use Cases for Continuous Query Caching
Continuous Query Caching is ideal for many use cases, such as event processing and instant access to up-to-date query results. - Understanding the Continuous Query Caching Implementation
The Coherence implementation of Continuous Query is found in theContinuousQueryCache
class. - Defining a Continuous Query Cache
Continuous query caching requires an underlying cache and a query filter. - Cleaning up Continuous Query Cache Resources
If a Continuous Query Cache is only used for a period of time, then the application must call therelease
method. - Caching Only Keys Versus Keys and Values
When constructing a Continuous Query Cache, you can 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
A client can place one or more event listeners onto a Continuous Query Cache. - Making a Continuous Query Cache Read-Only
A Continuous Query Cache can be made into a read-only cache by using the booleansetReadOnly
method on theContinuousQueryCache
class.
Parent topic: Creating C++ Extend Clients
Overview of Performing Continuous Queries (C++)
A continuous query cache is similar to a materialized view in the Oracle database. A materialized view copies data queried from the database tables into the view. If there are any changes to the data in the database, then the data in the view is automatically updated. Materialized views enable you to see changes to the result set. In continuous query, a local copy of the cache is created on the client. Filters allow you to limit the size and content of the cache. Combined with an event listener, the cache can be updated in real time.
For example, to monitor, in real time, all sales orders for several customers. You can create a continuous query cache and set up an event listener that listens for any events pertaining to the customers. Coherence queries for all of the data objects on the grid that pertain to a particular customer and copies them to a local cache. The event listener on the query listens for any inserts, updates, or deletes that take place on the grid for the customer. When an event occurs, the local copy of the customer data is updated.
Parent topic: Performing Continuous Queries (C++)
Understanding the Use Cases for Continuous Query Caching
Consider using Continuous Query Caching in the following situations:
-
A Continuous Query Cache is an ideal building block for Complex Event Processing (CEP) systems and event correlation engines.
-
A Continuous Query Cache 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
NamedCache
API, 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. Note that while a Near Cache is invalidation-based, a Continuous Query Cache actually maintains its data 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.
Parent topic: Performing Continuous Queries (C++)
Understanding the Continuous Query Caching Implementation
ContinuousQueryCache
class. This class, like all Coherence caches, implements the standard NamedCache
interface, which includes the following capabilities:
-
Cache access and manipulation using the
Map
interface:NamedCache
extends theMap
interface, which is based on theMap
interface from the Java Collections Framework. -
Events for all object modifications that occur within the cache:
NamedCache
extends theObservableMap
interface. -
Querying the objects in the cache:
NamedCache
extends theQueryMap
interface. -
Distributed Parallel Processing and Aggregation of objects in the cache:
NamedCache
extends theInvocableMap
interface.
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: Performing Continuous Queries (C++)
Defining a Continuous Query Cache
The underlying cache can be any Coherence cache, including another Continuous Query Cache. The most straight-forward way of obtaining a cache is by using the CacheFactory
class. This class enables you to create a cache simply by specifying its name. It is created automatically and its configuration is based on the application's cache configuration elements. For example, the following line of code creates a cache named orders:
NamedCache::Handle hCache = CacheFactory::getCache("orders");
The query is the same type of query that would be used to query any other cache. The following example illustrates how you can use code filters to find a given trader with a given order status:
ValueExtractor::Handle hTraderExtractor = ReflectionExtractor::create("getTrader"); ValueExtractor::Handle hStatusExtractor = ReflectionExtractor::create("getStatus"); Filter::Handle hFilter = AndFilter::create(EqualsFilter::create(hTraderExtractor, vTraderId), EqualsFilter::create(hStatusExtractor, vStatus));
Normally, to query a cache, you could use a method from the QueryMap
class. For example, to obtain a snap-shot of all open trades for this trader:
Set::View vSetOpenTrades = hCache->entrySet(hFilter);
In contrast, the Continuous Query Cache is constructed from the ContinuousQueryCache::create
method, passing the cache and the filter:
ContinuousQueryCache::Handle hCacheOpenTrades = ContinuousQueryCache::create(hCache, hFilter);
Parent topic: Performing Continuous Queries (C++)
Cleaning up Continuous Query Cache Resources
release
method.Parent topic: Performing Continuous Queries (C++)
Caching Only Keys Versus Keys and Values
To specify that only the keys should be cached, pass false when creating the ContinuousQueryCache
; for example:
ContinuousQueryCache::Handle hCacheOpenTrades = ContinuousQueryCache::create(hCache, hFilter, false);
If necessary, the CacheValues
property can be modified after the cache has been instantiated; for example:
hCacheOpenTrades->setCacheValues(true);
Parent topic: Performing Continuous Queries (C++)
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
. This is 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.
Parent topic: Caching Only Keys Versus Keys and Values
Using ReflectionExtractor with Continuous Query Caches
When the Continuous Query Cache is configured to cache values, the use of the ReflectionExtractor
is not supported. This is because the ReflectionExtractor
does not support reflection in C++. In this case, you must provide a custom extractor. When the Continuous Query Cache is not caching values locally, the ReflectionExtractor
can be used since it does not perform the extraction on the client but instead passes the necessary extraction information to the cluster to perform the query.
Parent topic: Caching Only Keys Versus Keys and Values
Listening to a Continuous Query Cache
For example:
ContinuousQueryCache::Handle hCacheOpenTrades = ContinuousQueryCache::create(hCache, hFilter); hCacheOpenTrades->addFilterListener(hListener);
If your application has to perform some processing against every item that is in the cache and every item added to the cache, then provide the listener during construction. The resulting cache 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. One form of the factory create method of ContinuousQueryCache
enables you to specify a cache, a filter, and a listener:
ContinuousQueryCache::Handle hCacheOpenTrades = ContinuousQueryCache::create( hRemoteCache, hFilter, true, hListener);
By default, listeners to the Continuous Query Cache have their events delivered asynchronously. However, the ContinuousQueryCache
implementation does respect the option for synchronous events as provided by the SynchronousListener
interface.
This section includes the following topics:
Parent topic: Performing Continuous Queries (C++)
Avoiding Unexpected Results
There are two alternate approaches to processing the items in the Continuous Query Cache, both of which could yield unexpected and unwanted results. First, if you perform the processing and then add the listener to handle any later additions, then events that occur in the split second after the iteration and before the listener is added are missed. For example:
ContinuousQueryCache::Handle hCacheOpenTrades = ContinuousQueryCache::create(hCache, hFilter); for (Iterator::Handle hIter = hCacheOpenTrades->entrySet()->iterator(); hIter->hasNext(); ) { Map::Entry::View vEntry = cast<Map::Entry::View>(hIter->next()); // .. process the cache entry } hCacheOpenTrades->addFilterListener(hListener);
The second approach is to add a listener first, so that no events are missed, and then do the processing. Although, the same entry may appear in both an event and in the Iterator
. The events can be asynchronous, so the sequence of operations cannot be guaranteed.
ContinuousQueryCache::Handle hCacheOpenTrades = ContinuousQueryCache::create(hRemoteCache, hFilter); hCacheOpenTrades->addFilterListener(hListener); for (Iterator::Handle hIter = hCacheOpenTrades->entrySet()->iterator(); hIter->hasNext(); ) { Map::Entry::View vEntry = cast<Map::Entry::View>(hIter->next()); // .. process the cache entry }
Parent topic: Listening to a Continuous Query Cache
Achieving a Stable Materialized View
The Continuous Query Cache 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. Secondly, the Continuous Query Cache 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
Making a Continuous Query Cache Read-Only
setReadOnly
method on the ContinuousQueryCache
class.For example:
hCacheOpenTrades->setReadOnly(true);
A read-only Continuous Query Cache does not allow objects to be added to, changed in, removed from, or locked in the cache.
When a Continuous Query Cache has been set to read-only, it cannot be changed back to read/write.
Parent topic: Performing Continuous Queries (C++)