| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| = Using Continuous Queries |
| |
| :javaFile: {javaCodeDir}/UsingContinuousQueries.java |
| |
| A continuous query is a query that monitors data modifications occurring in a cache. |
| Once a continuous query is started, you get notified of all the data changes that fall into your query filter. |
| |
| All update events are propagated to the <<Local Listener,local listener>> that must be registered in the query. |
| Continuous query implementation guarantees exactly once delivery of an event to the local listener. |
| |
| You can also specify a remote filter to narrow down the range of entries that are monitored for updates. |
| |
| [CAUTION] |
| ==== |
| [discrete] |
| === Continuous Queries and MVCC |
| Continuous queries have a number of link:transactions/mvcc[functional limitations] when used with MVCC-enabled caches. |
| ==== |
| |
| |
| == Local Listener |
| |
| When a cache gets modified (an entry is inserted, updated, or deleted), an event is sent to the continuous query's local listener so that your application can react accordingly. |
| The local listener is executed on the node that initiated the query. |
| |
| Note that the continuous query throws an exception if started without a local listener. |
| |
| [tabs] |
| -- |
| tab:Java[] |
| [source,java] |
| ---- |
| include::{javaFile}[tag=localListener,indent=0] |
| ---- |
| tab:C#/.NET[] |
| [source,csharp] |
| ---- |
| include::code-snippets/dotnet/ContiniuosQueries.cs[tag=localListener,indent=0] |
| ---- |
| |
| tab:C++[] |
| [source,cpp] |
| ---- |
| include::code-snippets/cpp/src/continuous_query_listener.cpp[tag=continuous-query-listener,indent=0] |
| ---- |
| -- |
| |
| == Initial Query |
| |
| You can specify an initial query that is executed before the continuous query gets registered in the cluster and before you start to receive updates. |
| To specify an initial query, use the `ContinuousQuery.setInitialQuery(...)` method. |
| |
| Just like scan queries, a continuous query is executed via the `query()` method that returns a cursor. When an initial query is set, you can use that cursor to iterate over the results of the initial query. |
| |
| |
| [tabs] |
| -- |
| tab:Java[] |
| [source,java] |
| ---- |
| include::{javaFile}[tag=initialQry,indent=0] |
| ---- |
| tab:C#/.NET[unsupported] |
| tab:C++[] |
| [source,cpp] |
| ---- |
| include::code-snippets/cpp/src/continuous_query.cpp[tag=continuous-query,indent=0] |
| ---- |
| -- |
| |
| |
| == Remote Filter |
| |
| This filter is executed for each updated key and evaluates whether the update should be propagated to the query's local listener. |
| If the filter returns `true`, then the local listener is notified about the update. |
| |
| For redundancy reasons, the filter is executed for both primary and backup versions (if backups are configured) of the key. |
| Because of this, a remote filter can be used as a remote listener for update events. |
| |
| |
| [tabs] |
| -- |
| tab:Java[] |
| [source,java] |
| ---- |
| include::{javaFile}[tag=remoteFilter,indent=0] |
| ---- |
| |
| tab:C#/.NET[] |
| [source,csharp] |
| ---- |
| include::code-snippets/dotnet/ContiniuosQueries.cs[tag=remoteFilter,indent=0] |
| ---- |
| |
| tab:C++[] |
| [source,cpp] |
| ---- |
| include::code-snippets/cpp/src/continuous_query_filter.cpp[tag=continuous-query-filter,indent=0] |
| ---- |
| -- |
| |
| |
| [NOTE] |
| ==== |
| In order to use remote filters, make sure the class definitions of the filters are available on the server nodes. |
| You can do this in two ways: |
| |
| * Add the classes to the classpath of every server node; |
| * link:code-deployment/peer-class-loading[Enable peer class loading]. |
| ==== |
| |
| |
| == Remote Transformer |
| |
| By default, continuous queries send the whole updated object to the local listener. This can lead to excessive network usage, especially if the object is very large. Moreover, applications often need only a subset of fields of the object. |
| |
| To address these cases, you can use a continuous query with a transformer. A transformer is a function that is executed on remote nodes for every updated object and sends back only the results of the transformation. |
| |
| [tabs] |
| -- |
| tab:Java[] |
| [source,java] |
| ---- |
| include::{javaFile}[tag=transformer,indent=0] |
| ---- |
| tab:C#/.NET[unsupported] |
| tab:C++[unsupported] |
| -- |
| |
| [NOTE] |
| ==== |
| In order to use transformers, make sure the class definitions of the transformers are available on the server nodes. |
| You can do this in two ways: |
| |
| * Add the classes to the classpath of every server node; |
| * link:code-deployment/peer-class-loading[Enable peer class loading]. |
| ==== |
| |
| == Events Delivery Guarantees |
| |
| Continuous queries ensure the exactly-once semantic for the delivery of events to the clients' local listeners. |
| |
| Both primary and backup nodes maintain an update queue that holds events that are processed by continuous queries |
| on the server side but yet to be delivered to the clients. Suppose a primary node crashes |
| or the cluster topology changes for any reason. In that case, every backup node flushes the content of its update |
| queue to the client, making sure that every event is delivered to the client's local listener. |
| |
| Ignite manages a special per-partition update counter that helps to avoid duplicate notifications. Once an entry in |
| some partition is updated, a counter for this partition is incremented on both primary and backup nodes. The value of |
| this counter is also sent along with the event notification to the client. Thus, the client can skip already-processed |
| events. Once the client confirms that an event is received, the primary and backup nodes remove the record for this event |
| from their backup queues. |
| |
| |
| == Examples |
| |
| The following application examples show typical usage of continuous queries. |
| |
| link:{githubUrl}/examples/src/main/java/org/apache/ignite/examples/datagrid/CacheContinuousQueryExample.java[CacheContinuousQueryExample.java,window=_blank] |
| |
| link:{githubUrl}/examples/src/main/java/org/apache/ignite/examples/datagrid/CacheContinuousAsyncQueryExample.java[CacheContinuousAsyncQueryExample.java,window=_blank] |
| |
| link:{githubUrl}/examples/src/main/java/org/apache/ignite/examples/datagrid/CacheContinuousQueryWithTransformerExample.java[CacheContinuousQueryWithTransformerExample.java,window=_blank] |