Skip Graph の concurrent JOIN を考える - その2
id:kibayos さんの PDF「構造化オーバレイの一貫性保証」 に書かれている Skip Graph の一貫性保証の例を読み解く。
http://live-e.naist.jp/sensor_overlay/3/doc/yoshida.pdf
その1の続き。双方向リンクリストに conncurrent Join / Remove する話。
その1 で述べたように下位レベルと当該レベルで、Join / Remove が発生する事を考慮しないといけない。
まずは Join から。
資料 P16 。
左側のノードをロックする事で実現。自分は「当該ノードの全てのレベル」をロックするものと解釈した。
[X] <-> [Y]
の 間に [A] を Join するケースを考える。
資料に書かれている手順は以下の通り。
- step1. add A->X
- step2. (lock at X)
- step3. add A->Y
- step4. change Y->X to Y->A
- step5. change X->Y to X->A
- step6. (unlock at X)
各 step で発生する conncurrent ならではの事象を考えてみる。
step1. add A->X
X がそのレベルで Remove されてしまうケース。
この場合は
- 削除された事が分かる可視なフラグ
- そのレベルでの A の隣接ノードを再度検索し、そのレベルで処理をやり直す必要がある。
step2 の lock を先にやっておけば問題ないのでは?。
[X] と [A] の間に下位レベルで Join が発生するケース。
step2 の lock を先にやっておけば問題ないのでは?。
step2. (lock at X)
- ロック獲得失敗(誰かがロックしている)
- X が削除された(これもロック失敗か)
が考えられる。この場合は wait するか、エラーを返す。
エラー処理が楽なのは wait 。
作業中に[X] と [A] の間に、ノードが Join する可能性があるので、ロック後に [X] の右が [Y] ではなかった場合、処理をやり直す。
step3. add A->Y
- Y が削除される。
- A が削除される
が起こる可能性がある。
Y が削除された場合はそのレベルでの Join を一からやり直す。
A が削除されるのは、検索で A が reachable な場所にいる場合。こういう場合はあるだろうか。
X, Y から A がリンクされていないので、そのような状況は発生しない。
step4. change Y->X to Y->A
[Y] が削除される可能性がある。この場合はそのレベルでの Join を一からやり直す。
[X] がロックされているので [Y] の左ノードが変更される事はない。
step5. change X->Y to X->A
A が削除される可能性がある。なぜならばそのレベルで Y->A というリンクがあるから。
と思ったけど、X がロックされているので A の削除は失敗するはず。なので OK 。
step6. unlock
Ok
ロックの実現方法
Erlang の gen_server はリクエストが serialize される事を利用すれば良さそう。
State にロックしているプロセス(もしくはロックを待っているプロセスの一覧)を持っておき、link_op の際に参照し From とマッチングする。
まとめ
concurrent Join に必要な機能は
- ノードのロック
- insert 失敗のエラー通知
- リトライの仕組み
- どこにいれるべきか?大本の Join 司る部分かな。
の 3 つ。
その 3 に続く。