- 物理機リージョン/AZ
- ハイパーバイザVM exit で捕まえる
- VMゲストOS
- コンテナnamespace / cgroup
- プロセスアプリ / DB今ここ
プロセスがファイルへ書いたものは、どこまで届いているのか。その境界を、規定と公式文書から確かめます。
まず結論
`write` が成功を返してもディスクに届いた保証はない、は第23週で確認済みです。本レッスンはその先。`fsync` が待つのは**デバイスが転送完了を報告すること**で、しかも `fsync` しただけでは、ディレクトリの項目が届いた保証は付きません。
もう一つ。**ext4 のジャーナリングは既定でデータを守っていません。** 通るのはメタデータだけで、公式文書自身が「クラッシュ後、データブロックはいかなる整合状態も保証されない」と書いています。
ext4 は既定でジャーナリングが有効です。電源が落ちたとき、直前に書いたファイルの中身はどうなりますか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
既に知っていることから始める
第23週『届いたことを確かめる — ACKと再送、そして規定と実装のズレ』で、write が成功を返してもデータはページキャッシュに入っただけで、保持されたと判断するには fsync の戻りを待つ必要があることを見ました。繰り返しません。本レッスンが足すのは、fsync は何を待つのか、fsync してもまだ届いていないもの、そしてジャーナリングは既定ではデータを守っていないことの3つです。
- ・薄い箱が本レッスンの主題。ファイルを fsync しても、その名前は別
- ・ジャーナルは図の外側で、ファイルシステムの構造を守っている
fsync は「書く仕事」を待っているのではない
fsync(2) の規定はこう書いています。
The call blocks until the device reports that the transfer has completed.
待つ相手はデバイスです。カーネル内部の手続きではなく、デバイスからの完了報告。直前にはこうも書かれています。
This includes writing through or flushing a disk cache if present.
ディスク側のキャッシュを書き抜けるかフラッシュするまでが範囲。
この性質は所要時間に現れます。16 MiB を 1 MiB ずつ 16 回書いたあと fsync を呼びました。
-- run 1 --write 16MiB : 2.946 msfsync : 7.308 msratio fsync/write = 2.5x-- run 2 --write 16MiB : 2.927 msfsync : 7.007 msratio fsync/write = 2.4x-- run 3 --write 16MiB : 2.721 msfsync : 7.417 msratio fsync/write = 2.7x
16 MiB 分の write が 3 ミリ秒弱で返り、そのあとの fsync 1 回が長い。単発ではさらに開きます。
openat(AT_FDCWD, "/tmp/s.bin", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE|O_CLOEXEC, 0644) = 3 <0.000054>write(3, "hellohellohellohellohellohellohe"..., 5000) = 5000 <0.000045>fsync(3) = 0 <0.000703>
5000 バイトしか書いていないのに、write の 45 マイクロ秒に対し fsync は 703 マイクロ秒。所要時間が「書いた量」で決まっていない。待つのは仕事量ではなく、デバイスの返事だからです。
`fsync()` を呼んでそれが返った。このとき確実に言えるのはどれですか。
中身が届いても、名前はまだ届いていない
同じ DESCRIPTION に、見落とされやすい段落があります。
Calling fsync() does not necessarily ensure that the entry in the directory containing the file has also reached disk. For that an explicit fsync() on a file descriptor for the directory is also needed.
ファイルを fsync しても、ディレクトリの項目が届いたとは限らない。ディレクトリの fd への明示的な fsync が別途要る、と規定が述べています。
新規作成では届けるものが2つ。中身と、そこへ到達する名前です。「保存した」の範囲は、どの fd に fsync を呼んだかで決まります。
なお fdatasync() は、後続の読み出しに必要でないメタデータのフラッシュを省きます。規定が挙げる例は st_atime / st_mtime です。
本命 — ジャーナリングは既定ではデータを守っていない
ext4 の公式文書 journal.rst は冒頭で目的をこう述べます。
Introduced in ext3, the ext4 filesystem employs a journal to protect the filesystem against metadata inconsistencies in the case of a system crash.
名指しされているのは metadata inconsistencies、メタデータの不整合。続く段落が帰結を書いています。
For performance reasons, ext4 by default only writes filesystem metadata through the journal. This means that file data blocks are /not/ guaranteed to be in any consistent state after a crash.
**既定でジャーナルを通るのはメタデータだけ。**その結果、クラッシュ後にデータブロックはいかなる整合状態も保証されないと公式が明記しています。保証する範囲も限定的です。
guaranteeing the atomicity of whatever gets written through the journal to the disk. The effect of this is to guarantee that the filesystem does not become stuck midway through a metadata update.
ジャーナル経由で書かれたものの原子性と、メタデータ更新の途中で止まらないこと。「中身が無事」とはどこにも書かれていません。
データモードは admin-guide/ext4.rst にあり、data=ordered に (*)(既定の印)が付きます。
data=journalAll data are committed into the journal prior to being written into themain file system. Enabling this mode will disable delayed allocationand O_DIRECT support.data=ordered (*)All data are forced directly out to the main file system prior to itsmetadata being committed to the journal.data=writebackData ordering is not preserved, data may be written into the main filesystem after its metadata has been committed to the journal.
既定の data=ordered は「データを本体へ先に押し出してから、メタデータをジャーナルに commit する」。順序を決めるだけで、データ自体はジャーナルに入りません。
同じ文書の commit オプションが、混同されやすい2語を書き分けています。
This setting limits the maximum age of the running transaction to 'nrsec' seconds. The default value is 5 seconds. This means that if you lose your power, you will lose as much as the latest 5 seconds of metadata changes (your filesystem will not be damaged though, thanks to the journaling).
既定は5秒。電源を失えば直近5秒ぶんのメタデータ変更は失うが、ジャーナリングのおかげでファイルシステムは損傷しない。「壊れない」と「失わない」は別だと公式が同じ文で書き分けています。
`/proc/mounts` を見たら ext4 の行に `data=ordered` が出ていませんでした。どう読むべきですか。
ext4 の既定のジャーナル(data=ordered)
ジャーナルを通るのはメタデータだけ。データは本体へ先に押し出され、そのあとメタデータが commit されます。公式文書は、クラッシュ後にデータブロックの整合は保証されないと明記しています。
DB編『先に書くログ — 順序を縛ると、書く回数が減る』の WAL
データベースは、データファイルを書き換える前に、その変更を記述したログを先に永続化します。本体への変更は、ログレコードが永続化された後でなければ書けません。記録の対象はデータそのものです。
ext4 のジャーナルと、データベースの WAL。どちらも「先にログを書く」仕組みですが、守っている対象はどう違うでしょうか。
それぞれが「ログに何を書くか」と「クラッシュ後に何を復元できると宣言しているか」を並べてください。
この章のまとめ
writeが成功を返してもページキャッシュにあるだけ、は第23週で確認済み。本レッスンはその先を扱った。fsyncが待つのはデバイスからの完了報告。ディスクキャッシュのフラッシュを含む。実測でも所要時間は書いた量に比例しなかった(比のみ。絶対値は VM 上の値)。- ファイルを
fsyncしても、ディレクトリの項目が届いた保証は付かない。 ディレクトリの fd への明示的なfsyncが別途必要。 fdatasyncが省くのは「後続の読み出しに必要でない」メタデータ(公式の例はst_atime/st_mtime)。- 本命。ext4 の既定は
data=orderedで、ジャーナルを通るのはメタデータだけ。 公式が「データブロックはクラッシュ後にいかなる整合状態も保証されない」と明記している。 - ジャーナルの保証は「経由して書かれたものの原子性」と「メタデータ更新の途中で止まらないこと」。「壊れない」と「失わない」は別。
/proc/mountsにdata=orderedが出ないのは既定だから省略された印。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
`fsync()` が「待っている」ものとして、規定に照らして最も正確なのはどれですか。
用語集(3語)
- ページキャッシュ
- ファイルの内容をメモリ上に保持する領域。`write` は既定でここまでで返る
- fsync / fdatasync
- ファイルの変更をデバイスへ反映させ、完了報告まで待つシステムコール。fdatasync は、後続の読み出しに必要でないメタデータのフラッシュを省く。
- ジャーナル(ext4)
- メタデータの不整合からファイルシステム自身を守るため、変更を先に書き留める領域。既定ではデータ本体はここを通らない。既定の data=ordered は、データを本体へ先に押し出してからメタデータを commit する。