Droonga 1.1.0での「hot add」の仕組みの説明
この辺りを見直して、「古いプロセスから新しいプロセスに主導権が移る」まさにその瞬間を境にして、それまでに来たメッセージはすべて古いプロセスが責任を持って処理しきるようになりました。これで、「いつの時点の書き込みバッファから有用で、いつ時点までが不要なのか」を判定可能になりました。
2015-05-03 09:53:44これらのすべての変更があってやっと、レコード追加のリクエストが流入し続ける状況下での、Droongaクラスタへの新規replicaノードのhot addが可能となりました。1つ1つ問題を解決すという感じで少しずつ歩を進めていたため、随分時間がかかってしまいました。
2015-05-03 09:58:38ともかくようやくこれで、「レプリケーション有りの、Groonga互換の全文検索システム」としてのスタート地点にちゃんと立てたと思います。
2015-05-03 10:01:17シャーディングありの構成でもノードの追加を簡単にできるようにするとか、Groongaの機能でまだ対応できてない機能に対応するとか、やらなきゃいけない事はまだまだ尽きませんが、とりあえずはこれが嘘偽りのないDroongaの最新の状況です。というご報告でした。
2015-05-03 10:04:06実際に行われる処理の流れから見た、Droonga 1.1.0でのhot addの仕組みの説明
需要ないけど続ける。ここまではDroonga 1.1.0で何をどう変えたか視点の話、ここからはDroonga 1.1.0でreplicaのhot add時に何がどういう順番で起こってるかという視点の話。
2015-05-03 11:05:37A,Bという2つの既存ノードと、新規にreplicaとして追加したいCの、計3つのノードがあるとします。droonga-engine-joinコマンドは、リモート操作のための命令を適宜発行してこれらのノードに順番に指示を与えて、hot addを実現します。
2015-05-03 11:08:37まず、A,Bどちらをデータのコピー元にするか決めます。これはどっちでもok(3ノード以上あるなら「どれか1つをデータのコピー元にする」ということになる)。仮にBをデータのコピー元、Aをサービス継続提供のための残存ノードにするとします。
2015-05-03 11:10:50最初は、Cのroleをabsorb-destinationに変更して、Cのcatalog.jsonにA,Bの情報を追加します。これでCのcatalog.jsonは最終的な状態に等しくなります。
2015-05-03 11:16:53この時点でまだA,BはCの存在を知らないため、A,Bが受信した検索リクエストもレコード追加のリクエストもCには配送されません。
2015-05-03 11:17:04次に、A,Bのcatalog.jsonにCの情報を登録します。これで、すべてのノードのcatalog.jsonが最終的な状態に等しくなります。と同時に、A,Bが受信したメッセージはCにも配送されうる状態になります。
2015-05-03 11:19:10が、実際にはメッセージはA,BからCへは配送されません。これはA,Bのroleがservice-providerなのに対して、Cのroleはabsorb-destinationだからです。配送先決定の段階で、roleの異なるノードは配送先から除外されます。
2015-05-03 11:21:37ただし、この時A,BからCは「レコード追加のリクエストだけは、後でまとめて転送するべき先」として認識されます。そこで、A,Bは自身の持つ書き込みバッファにC宛のレコード追加のリクエストを溜め込み始めます。
2015-05-03 11:24:04次に、Bのroleをabsorb-sourceにします。これで、Aが受信した各種リクエストはBにも転送されなくなり、A(残存ノード)だけがサービスを提供し続ける状態になります。
2015-05-03 11:26:28この時仮にBが検索やデータ追加のリクエストを受け取っても、roleがservice-providerではないため、roleがservice-providerである他のノード(今はAだけしかいない)にリクエストをbounceします。
2015-05-03 11:28:10こうして、サービスから切り離されたB(absorb-source、データベースには情報が詰まってる)とC(absorb-destination、データベースは空っぽ)が用意できました。いよいよデータコピーの開始です。
2015-05-03 11:29:56CはBに対して「データベースの内容をdumpして、C宛に送れ」というリクエストを送ります。Bは自分とroleが同じノードにだけそのリクエストを転送しようとしますが、Bしかいないので当然Bが処理し始めます。
2015-05-03 11:35:47dumpされたメッセージを受け取ったCは、やはりroleが自分と同じノードに対してだけメッセージを配送します。
2015-05-03 11:36:53こうして、同じクラスタに属しているにも関わらずBからCへのデータコピーが行われます。この間、残存ノード(A)の書き込みバッファにはB,C宛のレコード追加リクエストが溜まり続けます。
2015-05-03 11:38:22いっこ忘れてた。データのコピーが始まる前に、Bは自分の「最後に処理したメッセージのタイムスタンプ」を報告して、Cにはそれを「受付可能なメッセージのタイムスタンプの判断基準のタイムスタンプ」として登録します。
2015-05-03 11:56:48この情報はSerfを通じて全ノードに伝搬し、各ノードは「C宛にはいつ時点より後のメッセージだけを(書き込みバッファから)送出すればよい」ということをこの時点で把握します。
2015-05-03 11:57:54さて、データコピーが終わったら復帰処理です。Bのroleがabsorb-sourceからservice-providerに戻ります。これでAからB宛のメッセージ配送が行われるようになります。
2015-05-03 11:59:34この時まだ、AとBのデータベースの内容は同期が崩れています。Bのroleがservice-providerに戻ったことを受けて、AはB宛バッファに溜まっていたレコード追加のリクエストを送出し始めます。これをすべてBが処理しきると、A,Bのデータベースは同期した状態に戻ります。
2015-05-03 12:02:45最後に、Cのroleをabsorb-destinationからservice-providerに戻します。この時点でもやはり、A,BとCの間ではまだデータベースの同期が崩れています。
2015-05-03 12:04:13A,Bはそれぞれ、自身の持つC宛のバッファの内容を送出し始めますが、この時、先ほど通知された「Cにはいつ時点からのリクエストのみを送ればよいか」という情報を使って、BからCへのデータコピーで既に処理された後とみなしてよくなった有効期限切れリクエストは送出されず破棄されます。
2015-05-03 12:07:26