Skip to content

Droongaクラスタにノンストップでノードを追加する手順

piroor edited this page Nov 20, 2014 · 21 revisions

ノードを追加する操作において想定されるワークフローを検討する。

前提

  • node0/192.168.100.50
  • node1/192.168.100.51

の2つのレプリカがあると仮定する。 ここに

  • node2/192.168.100.52

を追加する。またこの時、

  • node1/192.168.100.51

を複製元として使う。

事前実装

実装しなければならない機能

  • last message time保持、graceful stop後に出力
  • effective message time保持、セット
  • serfのポート番号変更機能(デフォルト:7946、複製元:7947、複製先:7948)
  • バッファ機能の拡張
    • リードオンリーなメッセージはバッファに溜めない
  • メッセージ配送先決定処理の拡張
    • 一度ノードがliveでなくなった後、再びliveの状態に戻ったら、バッファが空になるまではリードオンリーのメッセージを配送しない。 バッファが空になったらリードオンリーのメッセージも配送し始める。
      • 書き込み頻度が高いとバッファがいつまでも空にならない。 バッファの残りが何個を切ったらバッファが空になったのと同じと見なしてリードオンリーのメッセージの配送を再開する、という風にするべきか? その場合、しきい値は誰が決めるか?

最後に受け取ったメッセージのtimestamp

  • droonga-engineは、最後に自分が受け取ったメッセージのtimestampを保持する。 (last_message_timestamp)
    • この情報はメモリ上の揮発性の情報として保持する。
    • プロセス終了時に、不揮発性の情報として出力する。
  • droonga-engineは、「これ以後のtimestampのメッセージのみ有効と見なす」「これ以前のtimestampのメッセージが来ても無視する」と判断する基準となるtimestampを保持できる。 (effective_message_timestamp:初期値=nil)
    • この情報は不揮発性の情報として与えられ、読み込んだ後はメモリ上の揮発性の情報として保持する。
    • effective_message_timestampが設定されている場合、入ってきたメッセージのtimestampをチェックして、古いメッセージであれば破棄する。

基本方針

  1. node2をクラスタに仮追加する。
  2. node1を元のクラスタから半分切り離す。
  3. node1からnode2へデータを複製する。
  4. node1, node2を元のクラスタに戻す。

step1: node2をクラスタに仮追加する

node2を以下の状態に設定する。

  • serfのポート番号は7948(複製先)。
  • catalog.jsonの内容は、node2のみからなるサブクラスタ。

その上で、すべての既存ノード(node0, node1)のcatalog.jsonを変更し、node2をクラスタに追加する。

これにより、以下の状態になる。

  • node2はserfの監視ポートが違うので、元のクラスタから見たら存在しないままである。
    • よって、node2は、node0, node1から見た時は死んだノードとして扱われるようになる。
    • 生存ノードからnode2へ配送される予定だったwriteなメッセージは、バッファに溜まり始める。

step2: node1を元のクラスタから半分切り離す。

node1を以下の状態に設定する。

  • serfのポート番号は7947(複製元)。
  • catalog.jsonの内容は、node1, node2のみからなるサブクラスタ。

これにより、以下の状態になる。

  • node1はserfの監視ポートが違うので、元のクラスタから見たらノードとして動作しなくなる。
  • node1は、node0から見た時は死んだノードとして扱われるようになる。
  • node1自身から見た時は、node1(生存)とnode2(停止中)のクラスタとなっている。
    • そのため、node1からnode2へ配送される予定だったwriteなメッセージはバッファ上に残っている。
  • node0からnode1とnode2へ配送される予定だったwriteなメッセージが、node0のバッファに溜まり始める。

step3: node1からnode2へデータを複製する。

serfでメッセージを送り、node2がnode1からデータを吸い出す。

データの複製が終わったら、node1のlast_message_timestampをnode2のeffective_message_timestampに設定する。

node1% timestamp=$(droonga-get-status --name=last_message_timestamp)
node2% droonga-set-status --name=effective_message_timestamp --value=$timestamp

step4: node1, node2を元のクラスタに戻す。

node0, node1を以下の状態に設定する。

  • serfのポート番号は7946(デフォルト)。
  • catalog.jsonの内容は、node0, node1, node2からなるサブクラスタ。

ここで、node1とnode2は、生存ノードから見た時に、ステータスが「死んでいるノード」から「復帰中のノード」に切り替わる。

  • node1はnode2に、バッファに溜め込んでおいたwriteなメッセージを配送し始める。
    • node2は、effective_message_timestampに基づいて、受け取る必要が無い有効期限切れのメッセージを破棄する。
    • effective_message_timestampよりも新しいメッセージを受信したら、「有効期限切れと見なすメッセージの時刻」はもう不要なので、破棄する。
  • node0はnode1とnode2に、バッファに溜め込んでおいたwriteなメッセージ配送し始める。
    • node2は、effective_message_timestampに基づいて、受け取る必要が無い有効期限切れのメッセージを破棄する。
    • effective_message_timestampよりも新しいメッセージを受信したら、「有効期限切れと見なすメッセージの時刻」はもう不要なので、破棄する。
  • node1, node2向けのバッファが空になるまでは、node1, node2宛のメッセージは引き続きバッファに貯まっていく。
    • バッファが空でない間は、リードオンリーのメッセージはそのノードには配送されリードオンリーのメッセージも配送され始める。

以上でノードの追加は完了である。

Clone this wiki locally