This chapter includes the following sections:
Tangosol.Net.Cache.ContinuousQueryCache
class.ContinuousQueryCache
class is used for continuous query caching and requires an underlying cache and a query filter.INamedCache
implementations, including ContinuousQueryCache
, should be explicitly released by calling the INamedCache.Release()
method when they are no longer needed, to free up any resources they might hold.Parent topic: Creating .NET Extend Clients
Coherence for .NET 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 for .NET Continuous Query is a cached query result that never gets out-of-date.
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 INamedCache
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 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 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.
Tangosol.Net.Cache.ContinuousQueryCache
class. This class, like all Coherence for .NET caches, implements the standard INamedCache
interface, which includes the following capabilities:Cache access and manipulation using the IDictionary
interface: INamedCache
extends the standard IDictionary
interface from the .NET Collections Framework, which is the same interface implemented by the .NET Hashtable
class.
Events for all objects modifications that occur within the cache: INamedCache
extends the IObservableCache
interface.
Querying the objects in the cache: INamedCache
extends the IQueryCache
interface.
Distributed Parallel Processing and Aggregation of objects in the cache: INamedCache
extends the IInvocableCache
interface.
Since the ContinuousQueryCache
class implements the INamedCache
interface, which is the same API provided by all Coherence for .NET caches, it is extremely simple to use, and it can be easily substituted for another cache when its functionality is called for.
ContinuousQueryCache
class is used for continuous query caching and requires an underlying cache and a query filter.The underlying cache is any Coherence for .NET 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:
INamedCache cache = CacheFactory.GetCache("orders");
The query is the same type of query that would be used to query any other cache; for example:
Filter filter = new AndFilter(new EqualsFilter("getTrader", traderid), new EqualsFilter("getStatus", Status.OPEN));
Normally, to query a cache, a method from the IQueryCache
is used; for examples, to obtain a snap-shot of all open trades for this trader:
ICollection setOpenTrades = cache.GetEntries(filter);
Similarly, the Continuous Query Cache is constructed from those same two pieces:
ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter);
INamedCache
implementations, including ContinuousQueryCache
, should be explicitly released by calling the INamedCache.Release()
method when they are no longer needed, to free up any resources they might hold.If the particular INamedCache
is used for the duration of the application, then the resources is cleaned up when the application is shut down or otherwise stops. However, if it is only used for a period, the application should call its Release()
method when finished using it.
Alternatively, you can leverage the fact that INamedCache
extends IDisposable
and that all cache implementations delegate a call to IDisposable.Dispose()
to INamedCache.Release()
. If you want to obtain and release a cache instance within a single method, you can do so by using a using block:
using (INamedCache cache = CacheFactory.GetCache("my-cache")) { // use cache as usual }
After the using block terminates, IDisposable.Dispose()
is called on the INamedCache
instance, and all resources associated with it are released.
To specify that only the keys should be cached, use the constructor that allows the IsCacheValues
property to be configured; for example:
ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter, false);
If necessary, the IsCacheValues
property can also be modified after the cache has been instantiated; for example:
cacheOpenTrades.IsCacheValues = true;
IsCacheValues 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 IsCacheValues
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.
This section includes the following topics:
For example:
ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter); cacheOpenTrades.AddCacheListener(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); foreach (ICacheEntry entry in cacheOpenTrades.Entries) { // .. process the cache entry } cacheOpenTrades.AddCacheListener(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.AddCacheListener(listener); foreach (ICacheEntry entry in cacheOpenTrades.Entries) { // .. process the cache entry }
However, the same entry may appear in both an event an in the IEnumerator
, 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 was added during or after the construction of the cache:
ContinuousQueryCache cacheOpenTrades = new ContinuousQueryCache(cache, filter, listener);
This section includes the following topics:
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 for .NET 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 Continuous Query Cache allows a developer to pass a listener during construction, thus avoiding exposing these same complexities to the application developer.
By default, listeners to the Continuous Query Cache have their events delivered asynchronously. However, the Continuous Query Cache does respect the option for synchronous events as provided by the CacheListenerSupport.ISynchronousListener
interface.
For example:
cacheOpenTrades.IsReadOnly = 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.