InotifyAndMakuosan

ファイルのミラーリングツールとしては rsync が最も使われていると思いますが、差分チェックの負荷が大きく、できればつかうのやめたいなー、と思っていたところ、Software Design 2009年8月号lsyncd というツールが載っていて、お、これはと思って見てみたのですが、仕組みとしては inotify + rsync なので、結局は rsync を裏で呼び出していて、差分チェックの呪縛からは逃れられません。

inotify でファイルの更新イベント受け取ってるんだから、さらに rsync で差分チェックする必要はないじゃん、と思うわけですが、なんらかの理由でイベントが受け取れない可能性もあるため、二重チェックした方が安全なんでしょうね。

でもやっぱり余分な負荷はかけたくない、ってことで思いついたのが、inotify で更新イベントを検知したファイルを、makuosan の msync コマンドに渡してミラーリングする方法。(makusan の詳しい解説は、WEB+DB PRESS Vol.51 に載ってます。)

そこで以下のようなコードを書いて動かしてみたところ、いい感じでファイルが同期できることを確認しました。

#!perl
#!/usr/bin/perl

use strict;
use warnings;
use File::ChangeNotify;

my $base = '/home/miya/work';
my $watcher = File::ChangeNotify->instantiate_watcher(
    directories => [ $base ],
);

while ( my @events = $watcher->wait_for_events() ) {
    for my $event ( @events ) {
        my $path = $event->path;
        $path =~ s!$base/!!;
        `msync --sync $path`;
    }
}

exit;

とは言っても、実運用で使うためには、イベントをとりこぼさない、またはとりこぼしてもリカバーするような仕組みを考えたり、負荷テストをしてみたりとか、色々と考慮しなければいけないことはありますが、それはまあおいおいってことで。

また、File::ChangeNotify がすべての inotify イベントに対応してるわけではなく、INCREATE, INMODIFY, IN_DELETE にしか対応してないため、パーミッション変更とかファイルの移動なんかは検知できない、という問題もあります。(パッチ書けばいけそうなので、書いてみるつもりです。)

File::ChangeNotify 自体は inotify だけじゃなく、他の仕組みにも対応できるようになっていて、inotify が使える Linux kernel 2.6.13 以降であれば File::ChangeNotify::Watcher::Inotify が、それ以外であれば File::ChangeNotify::Watcher::Default が自動的に呼ばれるようになってます。なので、Mac OS X の FSEvents に対応した File::ChangeNotify::Watcher::FSEvents とか書けば、Mac では自動的に FSEvents で更新イベントを検知、ってこともできそうです。

ともかく、rsync の負荷には困っているので、今後この辺の方法論は詰めていきたいな、と思っています。

追記

miyagawa さんによる File::ChangeNotify::Watcher::MacFSEvents が github にありました。ただし、FSEventsではファイル名とかイベントの種類はとれないそうです。Wikipedia 見てもそんな感じのことが書いてますね。