続・ext3でnano second

id:hirose31:20060221の続編。

パッチのキーワード(big inode、EXT[23]_GOOD_OLD_INODE_SIZE、inode_sizeあたり)でe2fsprogsのソースを探索して、mke2fsのアンドキュメントなオプション-Iを発見。-Iはinodeのサイズを指定するもので、デフォルトで128、最大で1024を指定可能。

ためしに-I 256でmke2fsしてみる。

# mke2fs -j -I 256 /dev/sdb1
(snip)
# tune2fs -l /dev/sdb1 | grep 'Inode size'
Inode size:               256

ほんでもってmountしてファイルを作ってみる。

# mount -t ext3 /dev/sdb1 /nano
# ls --full-time /nano/foo
-rw-r--r--  1 root root 0 2006-02-28 13:05:26.615535176 +0900 /nano/foo
# stat /nano/foo
  File: `/nano/foo'
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: 812h/2066d      Inode: 12          Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2006-02-28 13:05:26.615535176 +0900
Modify: 2006-02-28 13:05:26.615535176 +0900
Change: 2006-02-28 13:05:26.615535176 +0900

nano secondキター!

ちなみに、XFSはなんもしなくてもnano sec。ReiserFSはv4でもnano secではないようです。

んで、なんでnano secondにこだわるかというとこういうことです。

NFSで、

  • 同じファイルに対して
  • ある一秒の間に
  • 複数の異なるNFSクライアントホストから
  • 同じサイズのデータを
  • 書き込んだ

な場合に、正しいデータが読み出せないからです。

具体的にはこうです。

NFSクライアントホストC1とC2があるとして、時系列に次の処理が行われました。

  • ある一秒の間に
    1. C1があるファイルからreadしたら「abc」を得ました。
    2. C1があるファイルに「abc」をwriteしました。
    3. C2があるファイルからreadしたら「abc」を得ました。
    4. C2があるファイルに「xyz」をwriteしました。

この後、数秒後に、C1があるファイルをreadすると、「xyz」が得られるのが正しいのですが、「abc」が得られてしまいます。(C2は正しく「xyz」が得られます)

原因はlinux/fs/nfs/inode.cのnfs_refresh_inodeにあります。ここでは、NFSサーバのファイルが変更されているかどうかを、

  • mtime
  • ファイルサイズ

で確認しています。

つまり、この2つの条件が同じであれば、ファイルは更新されていないとみなされて、NFSクライアントに保持されているキャッシュが使われます。これがC1で「xyz」ではなく「abc」がreadされる理由です。

ファイルサイズが同一になるのは場合によってはまぁしかたないとして、mtimeが同じになってしまうのは、NFSサーバ側のファイルシステムのmtimeの分解能が秒であることがその原因です。少なくともLinuxNFSサーバ/クライアントの実装ではNFS的に秒以下のmtimeを扱えるようになっているので、NFSサーバ側のファイルシステムを秒より小さいmtimeを扱えるものにしてあげればこの問題は発生しなくなります。

ほんじゃ、nano secondサポートしているXFSでいいじゃんとなるわけですが、ちょっとした事情でXFSだと不都合があるので、他のファイルシステムでnano secondサポートできないか調べていたわけです。

参考までに、nano second以外のとりあえず的なほかの対応方法は以下の通り。

  • ダミーデータをつけたりして、writeするごとにファイルサイズが異なるようにする。
  • writeするときにrename(2)してinodeを変える。
  • ロックしたりして、書き込み間隔を一秒以上あけるようにする。