subversionリポジトリのバックアップについて考える

d:id:torin:20090901
先日の実に素敵な経験とその後の地獄のような修復作業を踏まえ、subversionリポジトリのバックアップについて考察・検討してみた。

結論

svnsyncでバックアップを行う。
ただし、別スクリプトを追加することによるメンテが必要。

解説

リポジトリ破損の再構築に伴い、破損の詳細について調べてみた。
結果、以下二パターンの破損が確認出来た。

1.データ領域の破損

リポジトリ内の各ファイルの実データが破損した状態。
破損したファイルの実データを取得するコマンド(update/export/checkput等)および対象ファイルへの変更のcommitはエラーとなる。

対応策

dumpや該当リビジョンの取得でエラーとなるため、dumpを伴うバックアップ手法やsvnsyncであれば検出が可能。
また、リポジトリ自体に足してsvnadmin verifyを実行することでも検出できる。

2.ログ領域の問題

ログデータが破損した状態。
今回の場合は、何故か1リビジョンのみログがEUCで保存されていた。
この場合、dump/loadは正常に可能だが、該当リビジョンに対してその他の作業コマンドはエラーとなる。
試してはいないが、localeを合わせればもしかしたら普通に作業出来たのかもしれない。

コミットした人の他のログは普通にUTF-8で保存されているし、何故特定バージョンのみEUCでログが保存されたのか、原因は不明。

対応策

前述したとおり、dump/loadは正常に行えるため、dumpを使うバックアップスクリプトやsvnadmin verifyではこの問題を検出できない。
このエラーが検出可能なバックアップツールは、実際にcommitを行うsvnsyncのみだった。


結局、1・2両方の問題を検出できるバックアップ手段はsvnsyncしかなかった。
2はかなり特殊な状況のため、数あるバックアップ手段からわざわざsvnsyncを選択する理由にならないと言ったらそれまでですが。


ちなみに、(svnsyncに限らずsubversionリポジトリ差分バックアップスクリプト全般に言えることだが)subversion差分バックアップとは、リビジョン間の差分を取るだけであって、リポジトリのデータ差分を取る訳ではないことには注意。
つまり、subversionの管理していない差分、要はコミット後のログの書き換え等の属性変更については、そのままではリポジトリのデータをバックアップ先に反映することが出来ない
これらの属性変更を許可している場合は何らかの対策が必要となる。

一番手っ取り早いのは定期的にリポジトリフルバックアップ(svnsyncであればバックアップ先リポジトリの再構築)を行うことだが、今回はリポジトリが数十GBに及ぶサイズのため、回線占有率の問題もあって難しい。

svnsyncには、この問題への対応のため、指定したリビジョンの属性を丸ごとコピーする機能が存在する。
そこで、svnlook logでバックアップ元・先両方のログを取得して差分比較し、ログが更新されたリビジョンのみ属性を更新するスクリプトを組んで対応することにした。
(出来がアレなのでここには載せませんが)


あと、覚え書きとして、破損したリポジトリの復旧手順も書いておく。

リポジトリ復旧手順

まず、破損したリビジョン以外を-rオプションで範囲指定してdumpする。破損リビジョンより後はdumpコマンドに--incrementalを付けないとエラーで止まってしまう。

次に作業用リポジトリを作成し、破損リビジョン直前までloadする。
破損したファイルが各ブランチ・タグの末端なら、リビジョン番号の保持のため、ダミーリビジョンで差し替えておく。
(ダミーリビジョンの差し替えについては[的]今日の三角巾(2009-01-05)を参考に。
時間情報も元リポジトリから取得してダミーリビジョン登録用のdumpfileに記載しておいたほうが無難)
あとは正常リビジョンのdumpfileとダミーリビジョンを交互にloadしていく。


破損したファイルがリビジョンツリー中程の場合はかなりの悲劇が待っている。
subversionは変更については差分しか保持しないため、最悪1ツリー丸々吹っ飛んでしまう事もある。
コミット時の作業手順を出来る限り再現し、一つずつコミットし直すしかない。
時間情報はコミット後、元リポジトリのログ情報を元にsvn propコマンドによる属性変更で対応する。
最後に、ブランチ・タグ末端での破損ファイルのうち、復旧できた(もしくは別にバックアップを取っていた)ものをadd/commit。


ちなみに、ログの破損は、hotcopyで別の場所にリポジトリをcopyし、svnadmin propsetでsvn:log属性を上書きすることで対応した。