Pivotal GemFire のオープンソース版としてリリースされた Apache Geode ですが、GemFire と違い、Continuous Query(CQ)がサポートされていません*1。
ということで、CQ っぽいものを実装してみるアイディアその1ということで、先日こちらのブログエントリーで、クライアントサイド CQ ということで、クライアントコードを弄って実装してみました。
本ブログエントリーでは、その2として、本来の CQ の動作であるサーバーサイドでの実装をしてみたいと思います。
基本的には、キャッシュリスナーで OQL を実行するというアイディアは、その1と変わりませんが、それをサーバーサイドで実行するわけですが、結果をクライアントに送るために、キャッシュイベントが CQ に合致した場合は、別リージョンに当該データエントリーを put して、他方クライアントはそのリージョンにあらかじめサブスクライブしておけばイベントとして拾えるので、結果として CQ イベントを受信したのと同じような効果が得られる、というアイディアです。以下、サンプルコードです。
- サンプルサーバーコード: MyCQServer.java
- SerialExecutor: SerialExecutor.java
- サンプルサーバー cache.xml: cache.xml
- サンプルクライアントコード: MyCQClient.java
- サンプルクライアント cache.xml: client-cache.xml
SerialExecutor ですが、これは、サーバーサイドのキャッシュリスナー内で put とかリージョンオペレーションを行うと、プロパティー設定の conserve-sockets がデフォルトの true の状態だったり、複数リージョンにわたって put したりするとデッドロックすることがあるので、それを防ぐためにキャッシュリスナーのコールバック関数呼び出しを非同期処理するためのものです。本件の趣旨とは関係ないですが、念のためつけておきます。
クライアントのコードと cache.xml は、普通にサブスクライブしてキャッシュリスナーで反応させるという、特に CQ とは直接の関連性はない、普通の実装となります。
メリットとしては、CQ をサーバーサイドで実行し、条件に合致するものだけクライアントにイベント送信するということで、GemFire の CQ 同様、効率がよいことですね。デメリットというか、取り急ぎ作った実装のせいではありますが、static の嵐なので OQL の変更に弱いということですね。あと、個人的には、CQ の評価ロジックをもっと効率化できるんではないかと思ってます(OQL の結果としてバリューだけでなく、キーも返すようにして、CQ イベントのキーと比較して get してみたいなことすれば、OQL にヒットした結果に最悪総当たりで比較しなくてすむはず…)。
近いうちに、実際の CQ の API もぱくった改良版を作ってみようかなと思います。
*1:2017 年現在は、Apache Geode で CQ はサポートされています