rsyncの罠

rsyncの -u オプションには注意が必要というお話。どういうオプションかというと、

 -u, --update
  This  forces rsync to skip any files which exist on the destina-
  tion and have a modified time that  is  newer  than  the  source
  file.   (If an existing destination file has a modify time equal
  to the source file's, it will be updated if the sizes  are  dif-
  ferent.)

訳すと

 -u, --update
  このオプションはrsyncに、転送先に存在し、かつ変更時刻が転送元より
  新しいファイルをスキップさせます。もし変更時刻が同じでサイズが違う
  場合には、転送されます。

つまり、-u をつけると、ソース元のタイムスタンプが転送先より古いと更新されない。注意が必要なのは、比較に使われるタイムスタンプは、変更時刻(modification time; mtime)で、これは「当該ファイルの属性」で変更できる。

言い換えるとソース元のファイルのタイムスタンプ(mtime)が若返ったとき、それは転送されない。mtimeが若返ることがあるのか?と思いがちだが、バックアップから書き戻すと容易に発生する。たとえばsrcディレクトリからdstディレクトリへrsyncでコピーすることを考える。

> find .
.
./src
./src/abc.txt
./dst
./dst/abc.txt

こういう状態で、以下のようなことをしたとしよう。

  • ファイルabc.txtのバックアップを取る
  • 編集する(ちょっとテスト的にいじりたいなど)
  • rsyncを行う
  • バックアップからファイルを書き戻す
  • 再度rsyncを行う

実際にやってみると、こうなる。バックアップを取っていじって、rsyncを行う(バックアップしたファイルまで転送されているが、別ディレクトリに取っても本質は同じこと)。

> cp src/abc.txt src/abc2.txt
> jvim src/abc.txt
> rsync -auv src/ dst/
building file list ... done
./
abc.txt
abc2.txt

sent 333 bytes  received 92 bytes  850.00 bytes/sec
total size is 63  speedup is 0.15

バックアップから書き戻して、再度rsyncしてみる。

-u オプション付きのものはファイルリストから漏れており転送自体が予定されていない。(もっともファイルリストに入っても、-nオプションを指定しているから、転送されないけど)

> mv src/abc2.txt src/abc.txt
> rsync -n -auv src/ dst/
building file list ... done
./

sent 112 bytes  received 26 bytes  276.00 bytes/sec
total size is 42  speedup is 0.30

-uオプションを外すと、転送リストに追加されていることがわかる。

> rsync -n -av src/ dst/
building file list ... done
./
abc.txt

sent 118 bytes  received 32 bytes  300.00 bytes/sec
total size is 42  speedup is 0.28

これは、ファイルの移動の場合では、Modify Time(mtime)が保存されるため。そのため、バックアップを取ったあと、編集し、書き戻すと、こういうことが発生する。

mvする前(バックアップから書き戻す前)

> stat -x src/abc*.txt
  File: "src/abc.txt"
  Size: 21           FileType: Regular File
  Mode: (0700/-rwx------)         Uid: ( 1001/    user0)  Gid: ( 1001/    user0)
Device: 0,110   Inode: 7136817    Links: 1
Access: Sat Oct 25 00:07:35 2008
Modify: Sat Oct 25 00:07:35 2008
Change: Sat Oct 25 00:07:35 2008

mvした後(バックアップから書き戻した後)

> stat -x src/abc.txt
  File: "src/abc.txt"
  Size: 21           FileType: Regular File
  Mode: (0700/-rwx------)         Uid: ( 1001/    user0)  Gid: ( 1001/    user0)
Device: 0,110   Inode: 7136811    Links: 1
Access: Sat Oct 25 00:08:03 2008
Modify: Sat Oct 25 00:07:22 2008
Change: Sat Oct 25 00:08:20 2008

mv後に、Modifyの時刻が、00:07:35 → 00:07:22 と若返っていることに注意。

ここでは、シェルベースで操作したが、Samba経由での操作でも同じことが発生する。他のファイルシステム(NTFS)へバックアップし、書き戻した場合でも同じことが発生する。うっかりすると、ファイルの一部だけ転送されて、バージョンが異なるファイルが混在してしまうので要注意。

単純にトラフィックが少ないcpとして、rsyncを使う場合には、-uオプションをつけてはダメ。-uオプションは、以下に示すように相互にデータのやりとりをするような場合に使う。つまり、データ転送を素早く行う為のオプションではなく、タイムスタンプを記帳し同期を行うためのオプションなのだろうね。

  To synchronize my samba source trees I use the following Makefile targets:
  get: 
    rsync -avuzb --exclude '*~' samba:samba/ .
   put:
    rsync -Cavuzb . samba:samba/
   sync: get put

もっとも、この場合でもファイルのタイムスタンプが書き戻る問題には対応できないわけだが、*NIX上でファイルを普通にエディタでファイルをいじっているだけなら問題はおきない*1

コマンドのオプションは、奥が深い(奥が深い症候群...)と思ったそんな日のこと。

*1:もっともこういう用途ならバージョン管理システムを使った方が幸せになれると思うんだけど...