Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
このPRの狙い
異常終了するケースで、
EndOfRecords
(以下 EoR )が存在しないジャーナル領域が生じ、以降そのlusfファイルが読み込めなくなるというissue #27 を解決する。EoR が消失する問題は、メモリ上のジャーナルバッファを永続化する処理が完了しない(=途中で強制終了する)場合に起こりうる、ジャーナル領域が破壊される分かりやすい一例である。
こうしたジャーナル領域の破損の例としては、EoR の消失以外にも、特定のジャーナルレコードが中途半端に書き出されてしまい、再起動できなくなるといったこともある。
本PRは、ジャーナルバッファの書き出し処理を「安全に」行うことで、強制終了した場合にも正常なジャーナル領域に回復する、より一般的な解決を目指す。
より正確には、ジャーナルバッファの書き出し処理がatomicになるようにし、書き出しが完了してジャーナル領域が完全に更新されるか、または書き出しの途中で失敗した場合は書き出しを行う直前まで戻す。
本PRに関する前提知識
Issue27とは何か
Issue27は、大まかには次の二つの状況で EoR が消失することを表している:
状況1(ジャーナルバッファをメモリからdiskにflushする際の話)
この場合には、ジャーナル領域に EoR が存在せず、ジャーナル領域がどこまでか認識できないため起動に失敗する。
状況2(ジャーナル領域の末尾に到達したので先頭までシークする際の話)
この場合にも EoR が存在しないために、ジャーナル領域全体を認識できない。
このPRでの上にあげた状況への対応
状況1について
メモリバッファの書き出し時に、EoRを上書きすることになるメモリバッファ先頭512バイトの書き出しを後回しにする。すなわち
sync
による永続化を行う。(この瞬間、Diskには古いEoRと新しいEoRの2つが存在する)さて、以下2つの点について説明したい:
a. EoRは2つ存在しても良いのか?
b. 先頭512バイトを特別扱いするのはなぜか?
a. については、EoRは2つ存在しても問題ない。Cannylsは再起動時に、Diskに永続化されているジャーナル先頭領域から順にエントリを取り出し、最初にEoRに到達したところで処理をやめる。従って、EoRが2つあったとしても正常に起動することはできる。
メモリバッファの書き出しに完全に成功した場合は、すなわち3. の書き出しまで完了したならば、ジャーナル領域は正常に更新されることになる。
一方で3.の書き出し完了まで到達しなかった場合は、古いEoRまでをもってジャーナル領域と認識するが、これはメモリバッファの書き出しに成功しなかったことを考えると自然な挙動であるといえる。
先頭512バイトを特別扱いする理由は、現実の多くのHDDが512バイト=1セクタに対する書き込みに関してatomicになっているからである。実際には古いEoRを、新しいメモリバッファの先頭でatomicに上書きさえできれば良い。EoRの消去(上書き)がatomicに行われずに強制終了した場合は、ジャーナル領域として壊れてしまう。この最後の書き込みがatomicに行われるために、メモリバッファ全体の書き出しがatomicになるとも言える。
コードではどうなっているか
flush_write_buf
メソッドが、上記のアルゴリズムに沿うように変更されたhttps://github.com/frugalos/cannyls/pull/29/files#diff-5664d011af898065aad8d33346649d30R115
Cannylsを新しい挙動で使いたい場合には、以下のようにする
状況2について
状況1と同じように、新しい方のEoRを書き出してから、既存のEoRを
GoToFront
で上書きする。GoToFront
を書く前に、一度先頭にシークするGoToFront
を書くべき位置までシークによって戻り、書き出す。コードでの対応
上に述べた挙動でジャーナルレコードを追加する
safe_enqueue
メソッドを導入した:https://github.com/frugalos/cannyls/pull/29/files#diff-42653effaf88e3f0b6c1217764dc36aeR182
Cannylsを新しい挙動で使いたい場合には、以下のようにする
このPRの有効性の確認
issue27の検証用リポジトリにこのPRを取り込んだブランチ https://github.com/yuezato/cannyls_eor_vanish/tree/use_PR_for_issue27 を用いると、これまで発生していた
StorageCorrupted
エラーが生じなくなる。パフォーマンス低下について
状況1について
状況1では、これまでジャーナルバッファのflush時には
sync
を行っていなかったのに対して、この修正では1発のsync
が必要になるため、バッファflush時には50ms前後(一般的なHDDに対するfsyncはこの程度の時間を要する)のレイテンシ悪化が発生する。ジャーナルバッファのflushをユーザ層で意図的に大量に発生させるためには、埋め込みPUTしたバッファ上にあるデータに対するGETを行う場合に限られる。
Frugalosではfrugalos_raftの票管理やlog_prefix, log_suffixで埋め込みPUTを行うため、パフォーマンスが劣化する可能性がある。
状況2について
状況2でも新たに
sync
を行うため同様にレイテンシが悪化するが、ring状のジャーナル領域の末尾を指すtail pointerが一周する時にのみ発生するため、ほとんどの場合は問題がない。