ブロックストレージと共有ファイル
接続先と保存データの寿命、コピーの時点、共有後の同時更新をそれぞれ確認します。
説明
ブロックストレージは番号付きの領域を読み書きし、その上のファイル構造はOSなどが管理します。共有ファイルは複数の利用者が同じ名前のファイルへアクセスします。保存場所を共有できることと、同時更新が正しく調整されることは別の問題です。
512バイトのブロックを二つ持つファイルを使い、単一接続の付替えと保存時点のコピーを確認します。次に二つのプロセスから同じ共有ファイルを読み書きし、更新を失う例と、読取りから書込みまでをロックする修正を実行します。
接続先と保存データの寿命、コピーの時点、共有後の同時更新をそれぞれ確認します。
1単一接続のブロック領域へ書く
この領域は同時に一つのノードだけが接続する条件です。node Aを接続し、2番目の512バイトブロックへcheckpoint=6を書きます。
python3 volume.py init1024バイトの領域を用意する
python3 volume.py attach --node A単一の接続先をAにする
python3 volume.py write --node A --value 62番目のブロックへ値を書く開始オフセットは512で、改行を含む13バイトを書きました。ブロックの先頭番号0はオフセット0、番号1はオフセット512です。
2ファイル名ではなく位置にあるバイト列
ブロック領域の512バイト目から32バイトを16進数で表示します。
xxd -g 1 -s 512 -l 32オフセット512から32バイトを1バイトずつ表示する block.img左端の00000200は16進数のオフセット512です。63 68 65…がcheckpointの文字列で、0aが改行、その後は00です。この領域には、通常のファイル名やディレクトリを解釈するファイルシステムをまだ載せていません。
3同時に別ノードを接続する要求
Aが接続したまま、Bの接続を要求します。
python3 volume.py attach --node B既存接続がある状態でBの接続を要求するsingle attachment held by Aで拒否されます。この教材の単一接続条件では、同じ領域を二つのOSへそのまま接続しません。複数接続に対応する製品でも、上位のファイルシステムや書込み調整が別途必要です。
4接続を外して別ノードから読む
Aの接続を外し、Bへ付け替えて内容を読みます。
python3 volume.py detach接続を外してデータを残す
python3 volume.py attach --node BBへ接続する
python3 volume.py read --node B同じ保存領域の内容を読むcheckpoint=6が残っています。接続先を変えることと保存データを削除することは別です。Bが同じ領域を読むため、Aで書いたバイト列を確認できます。
5保存時点のコピーを作ってから更新
checkpoint=6の時点でコピーを作り、その後7へ更新します。この例では書込みを止めた状態の全体コピーを使います。
python3 volume.py snapshot書込みのない時点のコピーを作る
python3 volume.py write --node B --value 7コピー後に現在の領域を更新する
python3 volume.py read --node B現在の内容を確認する現在の領域は7ですが、snapshot.imgは6の時点です。実サービスのsnapshotでは、アプリのメモリ内の未確定処理まで整合するかも考慮する必要があります。
6コピーから戻すと更新後の値は戻らない
保存したコピーで領域を復元します。
python3 volume.py restore保存時点のコピーへ戻す
python3 volume.py read --node B復元された時点を確認するcheckpoint=6へ戻ります。コピー後の7はこの復元元にはありません。復元できることだけでなく、どの時点まで戻るかを確認します。
7二つのプロセスから同じファイルを読む
共有ファイルの例では、AとBの処理が同じshared.txtを参照します。初期値6を両方から読みます。
python3 shared.py init共有ファイルへ初期値を書き込む
python3 shared.py read --node AAの処理から読む
python3 shared.py read --node BBの処理から読む両方でcheckpoint=6です。各ノードへ別々のコピーを配ったのではなく、同じ名前が同じ保存先を指しています。ただし、これだけで同時更新の整合性が保証されるわけではありません。
8同時に加算すると更新が失われる
二つのプロセスがともに6を読んでから、それぞれ7を書きます。書込みだけを順番にしても、読取り済みの古い値は変わりません。
python3 shared.py race二つのプロセスで古い値から加算する2回加算した期待値は8ですが、actual=7です。同じ保存先を共有していても、読取りと更新を一まとまりにしなければ、片方の更新が失われます。
9読取りから更新までをロックする
初期値6からやり直し、各プロセスが排他ロックの内側で読み、加算して書きます。
python3 shared.py locked読取りと書込み全体を排他制御して加算するactual=8になります。後のプロセスは先の更新後の7を読むためです。この例のロックは同じローカルファイル上で検査しており、ネットワークファイルサービスでは対応するロック機構と障害時の扱いも確認します。
まとめ
ブロックの接続先と、保存されたバイト列の寿命は別です。
復元できるのはコピーを取った時点の内容です。
共有ファイルの同時更新には、読取りから書込みまでを含む整合性の仕組みが必要です。
操作履歴と解説を振り返れます。
シェル / Python 3
python3 volume.py init
python3 volume.py attach --node A
python3 volume.py write --node A --value 6two 512-byte blocks created attached to A written offset=512 bytes=13
xxd -g 1 -s 512 -l 32 block.img00000200: 63 68 65 63 6b 70 6f 69 6e 74 3d 36 0a 00 00 00 checkpoint=6.... 00000210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
python3 volume.py attach --node Brejected: single attachment held by A
python3 volume.py detach
python3 volume.py attach --node B
python3 volume.py read --node Bdetached; block bytes retained attached to B checkpoint=6
python3 volume.py snapshot
python3 volume.py write --node B --value 7
python3 volume.py read --node Bsnapshot_sha256=e83a8f96058c3583d25db4e8c753a9ac7173f2db955f15fa99a851dc48bac160 written offset=512 bytes=13 checkpoint=7
python3 volume.py restore
python3 volume.py read --node Bblock restored from snapshot checkpoint=6
python3 shared.py init
python3 shared.py read --node A
python3 shared.py read --node Bshared checkpoint=6 node=A checkpoint=6 node=B checkpoint=6
python3 shared.py raceinitial=6 increments=2 expected=8 actual=7
python3 shared.py lockedinitial=6 increments=2 expected=8 actual=8
演習完了