はじめに
今回は、前回までに作成したグラフDBの使い勝手を良くするために、プロパティに対するCAS操作と隣接リレーションシップ取得のカーソル機能を追加したいと思います。
対象読者
- HBaseを使ってみたいけどどう使ったらよいか分からない方
- MySQLなどのRDB以外のデータベースを使ってみたい方
要件定義
グラフDBに以下の機能を追加します。
- プロパティに対するCAS操作
- 隣接リレーションシップ取得のカーソル機能
これらの機能を実現するために、以下のメソッドを追加します。
// ノードのプロパティのCAS操作 boolean checkAndUpdateNodeProperties(String nodeId, Map<String, String> expectProperties, Map<String, String> putProperties, Set<String> deletePropertyNames) throws IOException; // リレーションシップのプロパティのCAS操作 boolean checkAndUpdateRelationshipProperties(String startNodeId, String type, String endNodeId, Map<String, String> expectProperties, Map<String, String> putProperties, Set<String> deletePropertyNames) throws IOException; // カーソルを作成する(最新順) Cursor getCursor(String nodeId, String type, Direction direction) throws IOException; // カーソルを作成する(セカンダリインデックスを使用) Cursor getCursor(String nodeId, String type, Direction direction, Filter filter, Sort sort) throws IOException; // カーソルをフェッチする List<Relationship> fetch(Cursor cursor, int length) throws IOException;
checkAndUpdateNodePropertiesとcheckAndUpdateRelationshipPropertiesがCAS操作に関するもので、getCursor、fetchがカーソル機能に関するものです。
checkAndUpdateNodeProperties(ノード)とcheckAndUpdateRelationshipProperties(リレーションシップ)は、現在のプロパティがexpectPropertiesと一致した場合に、更新(putProperties)と削除(deletePropertyNames)を行います。
getCursorはカーソルオブジェクトを取得するメソッドですが、最新順で取得するものとセカンダリインデックスを使用するものの2種類あります。
getCursorで取得したカーソルオブジェクトを指定してfetchを呼ぶことで隣接リレーションシップを取得する流れです。
同じカーソルオブジェクトに対してfetchを複数回呼ぶことで、隣接リレーションシップを頭から順に取得することができます。
今回は、スキーマの変更がありませんので論理設計と物理設計は省略します。
実装
それでは、実際にコードを書いていきます。GitHubにソースコードを上げていますので、こちらをベースに説明していきます。また、今回も前回作成したクラスを継承する形で実装をしています。
ノードのプロパティに対するCAS操作
最初にノードのプロパティに対するCAS操作をするコードです。GitHub上では以下のURLになります。
処理の流れは以下のようになります。
- 指定のノードの現在の更新時間、プロパティを取り出す(44~60行目)
- expectedPropertiesと現在のプロパティを比較する。もし、値が違っていたらCAS操作が失敗となりfalseを返す(63~67行目)
- 新しい更新時間、新しいプロパティを生成(70~87行目)(新しい更新時間は必ず、現在の更新時間より後になるようにする)
- 更新時間に対するcheckAndPutを行う(95行目)。falseが返ってきたら、(1)に戻る(PutのTimestampには新しい更新時間を指定する)
基本的には、ノードのプロパティの追加・更新・削除の処理と同じですが、CAS操作のためのプロパティ比較する2の処理が追加されています。
4の処理でプロパティの値に対してcheckAndPutを行っているのではなく、更新時間に対してcheckAndPutを行っているところがすこしややこしいかもしれません。
実際のチェック(2の処理)はHBase上ではなく、プログラム上で行っています。
更新時間に対するcheckAndPutが成功した場合、1の処理で取り出したプロパティに変更がないことが保証され、新しいプロパティがPutされるので、CAS操作が実現されます。