フロムスクラッチ開発者ブログ

from scratch Engineers' Blog

Apache ZooKeeperを内部解析してみる vol.4 〜znodeステータス編〜

こんにちはfukuです。

前回は実際にソースコードから内部的なデータノードの管理について解説を行いました。 今回は、前回少し紹介を行なったznodeのステータスについて詳細に説明を行なって行きます。

zxid

znodeのステータスの説明に入る前に、zxidについての説明を行います。ZooKeeperはもともと、複数のサーバにて分散して動作するように設計がされています。 そのため、各サーバが協調して動作するように、さらにはデータノードを定期的に永続化をする際に、データの整合性を保つためにzxid(ZooKeeperトランザクションID)というものがあります。 zxidはスタンドアローンとクラスタとの実行で少し役割の範囲が異なりますが、ここではスタンドアローンでの場合を考えます。 スタンドアローンでは主にデータの整合性を保つためにzxidを利用します。 そのためノードの追加や変更などのデータ更新を行うたびに、シーケンシャルに増加する値としてzxidがあるという認識で大丈夫です。

znodeのステータス

ではここで、znodeを操作することでどのようにステータスが変化するのかを見て行きましょう。

まず/testノードを作成し、ステータスを参照します。

[zk: localhost:2181(CONNECTED) 0] create /test "hoge"
Created /test
[zk: localhost:2181(CONNECTED) 1] get -s  /test
hoge
cZxid = 0x9
ctime = Sat Mar 31 22:27:40 JST 2018
mZxid = 0x9
mtime = Sat Mar 31 22:27:40 JST 2018
pZxid = 0x9
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0

cZxidでは対象のznodeを作成した時のzxid、ctimeはznodeを作成したシステム時間がそれぞれ記録されます。 上記から/testノードを作成した時のzxidは0x16であることがわかります。

次にmZxidmtimeは、それぞれznodeが最終更新された時のzxid、システム時間となるので、以下のように実際に更新してみます。

[zk: localhost:2181(CONNECTED) 2] set /test "hogehoge"
[zk: localhost:2181(CONNECTED) 3] get -s  /test
hogehoge
cZxid = 0x9
ctime = Sat Mar 31 22:27:40 JST 2018
mZxid = 0xa
mtime = Sat Mar 31 22:28:01 JST 2018
pZxid = 0x9
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 0

znodeを更新することでmZxidmtimeが変更されていることがわかります。またzxidの値もインクリメントされてセットされていることが確認できます。

続いてpZxidですが、こちらは子ノードを最終更新したzxidとなります。またcversionは子ノードのバージョン番号となっていて、子ノードが変更されるたびにバージョン番号が変更されます。

[zk: localhost:2181(CONNECTED) 4] create  /test/child1 "child1"
Created /test/child1
[zk: localhost:2181(CONNECTED) 5] get -s  /test
hogehoge
cZxid = 0x9
ctime = Sat Mar 31 22:27:40 JST 2018
mZxid = 0xa
mtime = Sat Mar 31 22:28:01 JST 2018
pZxid = 0xb
cversion = 1
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 1

注意しなければならないのは、pZxidcversionはあくまで直属の子ノードに対するものであるので、孫にあたるznodeに対してはステータスの変更は起こりません。

[zk: localhost:2181(CONNECTED) 6] create  /test/child1/grandchild1 "grandchild1"
Created /test/child1/grandchild1
[zk: localhost:2181(CONNECTED) 7] get -s  /test
hogehoge
cZxid = 0x9
ctime = Sat Mar 31 22:27:40 JST 2018
mZxid = 0xa
mtime = Sat Mar 31 22:28:01 JST 2018
pZxid = 0xb
cversion = 1
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 1

続いてdataVersionですがこちらはznodeに格納するデータのバージョンになります。このdataVersionを比較することによって、他のクライアントが対象データを書き換えを行ったかをチェックすることができます。

znodeに対してデータの書き込みを行う際に、バージョンを指定するオプションがあり、指定したバージョンが現在のdataVersionと一致する時のみ、書き込みを行うことができます。これによって他のクライアントが書き込みを行った場合には、書き込みを避けると行ったことが可能になります。

[zk: localhost:2181(CONNECTED) 8] set -v 10 /test hogehogehoge
version No is not valid : /test

[zk: localhost:2181(CONNECTED) 9] set -v 1 /test hogehogehoge

[zk: localhost:2181(CONNECTED) 10] get -s  /test
hogehogehoge
cZxid = 0x9
ctime = Sat Mar 31 22:27:40 JST 2018
mZxid = 0x20
mtime = Sat Mar 31 22:56:32 JST 2018
pZxid = 0xb
cversion = 1
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 1

aclVersionはデータノードに設定されているアクセス権のバージョンを示す値となります。以下のようにデータノードのACLを変更することでaclVersionの値が更新されます

[zk: localhost:2181(CONNECTED) 11] setAcl /test world:anyone:crdwa
[zk: localhost:2181(CONNECTED) 12] get -s /test
hogehogehoge
cZxid = 0x9
ctime = Sat Mar 31 22:27:40 JST 2018
mZxid = 0x20
mtime = Sat Mar 31 22:56:32 JST 2018
pZxid = 0xb
cversion = 1
dataVersion = 2
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 12
numChildren = 1

ephemeralOwnerはEPHEMERALモードのデータノードに対してセッションIDを格納ために利用されます。これによってZookeeperは特定のクライアントのセッションが切断された時に、対象のセッションIDとEPHEMERALモードのデータノードに格納されているephemeralOwnerを比較して一致すれば、そのデータノードを削除します。

# zkCliの起動ログ。セッションIDが0x1000363a57c0007に設定されている
2018-03-31 22:52:47,599 [myid:localhost:2181] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1381] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x1000363a57c0007, negotiated timeout = 30000

[zk: localhost:2181(CONNECTED) 0] create -e /ephemeral
Created /ephemeral

[zk: localhost:2181(CONNECTED) 1] get -s  /ephemeral
null
cZxid = 0x25
ctime = Sat Mar 31 23:14:16 JST 2018
mZxid = 0x25
mtime = Sat Mar 31 23:14:16 JST 2018
pZxid = 0x25
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x1000363a57c0007
dataLength = 0
numChildren = 0

最後にdataLengthnumChildrenの値ですが、こちらは文字の通りで、データノードに格納されているデータのバイト数と、子ノードの個数をそれぞれ表しています。

おしまいに

今回はデータノードのステータス管理について記述していきました。特にzxidやバージョン管理を行うことで、複数のクライアントがデータノードに関しての操作を行っても協調しながら状態管理が行えるようになります。