-
-
Save rimms/9a4586bde31047ec96db to your computer and use it in GitHub Desktop.
ユースケース | |
============ | |
1. ヒューマンエラーなどの意図せぬ状態変更からの復元 | |
2. システムとしてのバックアップ | |
3. システム状態のコピー(テスト用など) | |
想定される取得タイミング | |
======================== | |
1. ユーザーによる手動実行 | |
2. 自動実行 | |
要求事項(goal) | |
============== | |
* スナップショットからシステム状態を「安全に」復元したい | |
* 「安全に」とは、機械学習的、システム的に正しく動作する状態ということ | |
* client から keeper がリクエストを受け付けた瞬間の全ノードの状態を出力したい | |
-> 完全性を保持するためには、クラスタ全体の update を止める? | |
=> [参考] HDFSのスナップショット | |
http://hadoop.apache.org/docs/r1.0.4/hdfs_design.html#Snapshots | |
http://hortonworks.com/blog/snapshots-for-hdfs/ | |
https://issues.apache.org/jira/browse/HDFS-2802 | |
-> 3.0.0 でリリース予定 | |
* 簡単な方が良い。 | |
決めるべきこと | |
============== | |
* 完全性の確保 | |
-> どこまでの完全性を確保するか | |
-> ノード間の時間差は許容されるのか、時間差により問題は発生しないのか | |
-> 不完全な snapshot と redoログ的なもの との組み合わせ? | |
スナップショット方法(案) | |
======================== | |
A) 完全なスナップショットを取得する | |
[1] save リクエスト実行 | |
[2] keeper から zookeeper のマスターロックを取得 | |
1) すべての keeper で update リクエストをブロック(saveは可能) (メンテナンスコードをレスポンス?) | |
2) server 間の mix もブロック | |
[3] ready-to-save 状態となる | |
[4] save処理実行 | |
1) zookepper から membership 情報の取得/出力 | |
2) zookeeper から config 情報の取得/出力 | |
3) server から serialize した model の出力 (非同期) | |
[5] マスターロック解除 | |
[6] finished-to-save 状態となる | |
メリット: 特定のタイミングでの完全なスナップショットをとれる | |
デメリット: 可用性を失う、update 性能へ影響を与える | |
B) 適切なスナップショットを取得する | |
[1] save リクエスト実行 | |
1) zookepper から membership 情報の取得/出力 | |
2) zookeeper から config 情報の取得/出力 | |
3) server から serialize した model の出力 (非同期) | |
メリット: 可用性を維持できる | |
デメリット: update や mix により、特定のタイミングでの完全なスナップショットとはならない | |
(運用で update を止めないと、完全性を保証できない) | |
(mix を一時的に止める仕組みの提供も必要となる) | |
保存対象 | |
======== | |
1. モデル ... 学習の状態 | |
2. fv_converter の状態 | |
3. config | |
-> 分散構成時は、zk へ保存したものを各ノードで再ロードする | |
4. クラスタ構成 | |
-> cht の場合、クラスタ構成に依存してデータの格納ノードが変わる | |
5. その他、状態を保持するもの (global_id など) | |
保存先ディレクトリ構造(案) | |
========================== | |
/datadir/snapshot-ID(or UUID) | |
+ Jubatus_TYPE_NAME (下記は記録する情報、NAMEがない場合は、_もなし) ※形式はJSONが楽かも | |
| - バージョン ... loadする際に起動中のServerのバージョンと比較する | |
| - タイムスタンプ ... 特に利用しない。付加情報。 | |
| - プロセス名 ... 付加情報 | |
| - config ... loadする際に起動時に指定されたconfigと比較する | |
| - membership ... zk上のactor情報と比較する | |
| - global_id | |
+ model ... 現状の形式を踏襲 | |
+ fv_converter | |
※同一の情報を利用することを保証するため、UUIDなど一意性を保証できるIDを付与する(現行の ID のままで、運用ルール化?) | |
対応スケジュール | |
================ | |
0.4.1 (or 2) : 対象情報を save できるようにする (案B) | |
0.5.0 以降 : A案のような排他制御機構を実装し、完全なスナップショットを取得可能とする | |
過去の記述(メモ) | |
================ | |
* やりたいこと | |
+ save: | |
- Jubatus のインメモリ情報を永続化する | |
- Jubatus の処理性能へ影響を与えないよう実現する (プロセスを fork して出力?) | |
- モデル情報を可視化したい (#178) | |
- 定期実行するようなツールを用意する? | |
+ load: | |
- 情報を読み込むことで、save したときの状態を復元する | |
- 引数指定することで起動時に読み込むことを可能とする (#222) | |
- config や model に変更があったことは確認する?? | |
* 出力内容 | |
1. config ... 現状: json形式 (string, json_config) | |
+ 読み込んだ際の string型 のものであれば、get_config で取得可能 ... 起動時読み込みなので get_config で十分という認識 | |
+ json型 に変換されたら unordered_map | |
-> print or pritty で string型 等に取得はできる | |
+ classifier_serv_config のような構造体に変換されたものを json型 に戻す? | |
-> 本来の意味での load された config はこれになる | |
2. model ... 現状: pficommon::data::serialization::binary_iarchive で serialize されたもの | |
+ 各 storage で変換を実施 | |
+ 解読可能にするには外側から デシリアライズ をする? | |
+ strage ごとの構造体を人間が解釈可能なものへ変換する | |
3. 状態を持つものは全体的に保存したい(global_id?, ) | |
4. そのうち window データとか? | |
+ どんな状態をどんな構造で持つのか? | |
* save/load 拡張案 | |
1. serialize したものをとりまとめて、出力する | |
2. ディレクトリを切って、それぞれ別ファイルに出力する | |
* 可視化案 | |
1. save とは別に可視化用の口を作る(dump) | |
-> format も検討 | |
2. save したものを可視化するツールを作る ... 難易度高 | |
3. save そのものをserialize ではなく、プレーンな形式にする |
zkが死活監視をするというの、実際のところ「zkに対して必ず指定時間以内に応答する」という条件を満たしているだけであって、たまたま知らずにzkへの応答が遅れて離脱と見做されアンロックされる場合を想定して処理を書くとなると更に面倒になります。特にデータセンタなどを跨ぐ将来を考えるとzkへの応答時間に制約を設けるのはあまり好ましくないなぁと考えます。
で、kがいくつずれても良いかという議論ですが、分散ストレージの議論を持ってくるなら「因果関係が壊れなければどこまでずれてもOK」というのが答えです。あるクライアントがA→B→Cの順番で一個ずつ保存していく過程なら、それに対するスナップショットは、[],[A],[A,B],[A,B,C]のどれかしか許されません。つまり例えば[B]というスナップショットを取ることはNGなわけです。が、A・BがCHTで別のサーバ(それぞれα・β)に割り当てられる場合に、それらを保存する場合に
- αを観測→[] (これを①とする
- A,Bを順に保存。αはAを保持、βはBを保持する。
- βを観測→[B] (これを②とする
という過程を経て、①と②を合成して[B]を全体のスナップショットとしてしまう事があるのは不味いです。
Jubatusの学習においてもそうやって外から因果関係を期待するケースはあると思うのでそこをどう許容するかですね。クライアント側にカウンタを付けてロックしましょうか…。
あとWait-free Snapshotですが、N回に1回の周期でupdate時にSnapshotを取ってから保存、という方法を取ればSnapshot側のstarvationを回避しつつパフォーマンスをある程度犠牲にせずに済む上国内学会の論文ネタぐらいにはなるんじゃないかと思うのですが微妙ですかね?
「zkに対して必ず指定時間以内に応答する」という条件を満たしているだけ
「コーディネータから離脱したがクラスタに参加している」というケースを想定する必要があるということでしょうか。
(すみません、こちらの理解が追い付いていません。。)
Jubatusの学習においてもそうやって外から因果関係を期待するケースはある
現時点では、updateリクエスト処理がすべて完了すればコンバージする(というか因果関係が保たれた状態になる)ことが保証されていて(=「ゆるやかな一貫性」?)、これはこれで良いような気がしています。
一方で、因果関係が崩れた状態で save されたモデルを load してもコンバージョンが保証できないので、これは問題です。
今出ている中で A 案の実現方法を考えてみると、以下のような感じでしょうか。(松竹梅は実装コスト)
- 松案: cht IDの更新単位(row)とモデルに世代番号を付与 (ノンブロッキング)
- モデルの世代番号は全ノードで共有されない(updateによってモデルが更新されるのはそのrowを持つノードのみ)なので、rowごとに世代番号を付与
- rowは削除される可能性があるため、各ノードごとのモデルにも世代番号を付与
- save中の可用性劣化なし
- 竹案: Keeperのupdateリクエスト処理区間をトランザクション化 + モデルに世代番号を付与 (ノンブロッキング)
- save中の可用性劣化なし
- 梅案: ブロッキング(ロックベース)の手法
- 単純に、mix/updateの新規開始/受付を禁止 -> 既存のmix/updateの完了待ち合わせ -> save
- save中の可用性は劣化(analysisは継続可能)
Wait-free Snapshot
ユースケース次第ですが、頻繁(ないし自動的)なsave が必要であれば、あってもよいと思います。
先行して実装を行う 保存対象の拡張 という点について、 https://gist.github.com/6ae462fbe27aacb53e4c に整理中。
「コーディネータから離脱したがクラスタに参加している」というケースを想定する必要があるということでしょうか。
そうです。
実際には反応が遅れただけなのに離脱したと見做されて勝手にロックが開放される事まで考慮した実装をするのは面倒そうという。確率の問題なので運用でカバーというのは無理ではないのですが。
ちょっと思いついたのですが
linear_mixerの場合はmixの瞬間に全体の一貫性を持ったデータが手に入るわけで、数秒に1度の頻度でmixするなら「一番最近のmixデータ」だけは常時保存し続けても良いんじゃないだろうか?と。
save対象がどこまで最新でなくてはいけないかは再考の余地がありそうです。
mix のタイミングで取得
確かに、liner_mixer であれば、一貫性のとれたモデルデータが取得できますね。
気になるのは liner_mixer 以外の mix戦略 をとったときには?? という点ですが、A案の実現方法として候補に入れさせて頂きます。ありがとうございます。
save対象が最新でなくてはいけないか
jubatus の緩い共有というコンセプトから考えても、保証する範囲を再考する必要がありますね。
皆様から頂いたコメントをもとに、今一度、整理を行いたいと思います。
以下にまとめました。なかなかのボリュームになりました。
https://gist.github.com/4633358
kumagiさん、kmaehashiさん、コメントありがとうございます。
ロックベースな手法におけるロックの抱え込み死の考慮、確かにそうですね。
zk上のロックは、ロック取得プロセスのダウンとともに解除される(はず)ですが、様々なパターンを想定した設計が必要です。
実装コスト、可用性を犠牲にすること(この点が一番の問題点で、利用ケースによっては update をブロックすることが許されないかもしれない)を考えると、ノンブロッキングな方式に倒すべきなのかもしれません。
※load の際のフローについては、検討不足ですが、全プロセスの load が終わるまで、update/mix をブロックする必要がある気がします。(むしろ、ブロックしなくてはいけない状況/ユースケースなので、許容されるのか?)
k=0 にすることができない update を頻繁に行うケースについては、k がある範囲になる形で諦めるしか無いのかもしれないです。(正しく動くという保証は...)
「1つの save結果であるべき」「管理ノードで集約して save する」ということについては、ユースケースの 3 と ディスクの問題の点では確かにその通りだと思います。
100台 save、2台 load に限らず、同一構成で load する場合も、ユースケース 3 の場合は、IPアドレスが違う可能性が高いので、リバランスが必要ですね。
[以下はあまり関係ないです]
現状の想定ユースケースは「ある時点 T の状態を復元する」という点ですが、「Jubatus に予期せぬ障害が発生しても、障害発生時点(最新の状態) の状態に復元可能」という点では、"場合によっては"、update の都度、永続化するような機構も必要とされてくるのかもしれません。(または、クラスタ内でのデータを多重度を選べるようにするとか)