テクノロジー · 実用ガイド
SQLiteバックアップは完了だけで十分?ローテーション前に再オープンして検証
バックアップ呼び出しの完了はコピー終了を示すだけです。候補ファイルを読み取り専用で再オープンし、SQLiteとアプリの検査後に昇格し、旧正常版は最後にローテーションします。

SQLiteバックアップ呼び出しの成功は復元受け入れの出発点です。一意の候補パスへ作成し、宛先を閉じ、その同じファイルを読み取り専用で再オープンして、検査が本番DBを見ていないことを確認します。
受け入れにはエンジンとアプリの両方の証拠が必要です。すべて通過してから対象ファイルシステム上で候補を昇格し、昇格後の再オープン成功まで直前の正常バックアップを保持します。
1. 最終ファイルではなく候補を作る
ソースを使用中のままにする場合や進行状況・ページ単位が必要な場合はオンラインバックアップAPIを使います。PythonではConnection.backup()です。VACUUM INTOも整合した圧縮済みスナップショットを作れますが、出力は新規または空である必要があり、中断すると不完全・破損ファイルが残る可能性があります。
昇格予定のディレクトリに一意の候補を作ります。稼働中のWAL DBでメインの.dbだけを盲目的にコピーしません。ファイルコピーを使う運用では、DBを安全に停止するか必要な付随ファイルをすべて扱います。
2. 正確な候補を読み取り専用で再オープン
宛先接続を閉じ、候補そのものを読み取り専用URIで開きます。ソース接続や本番DBに解決され得る別名パスで検査しません。昇格前にパス、サイズ、ハッシュ、SQLite/Python版、作成方法と結果を記録します。
CPython 3.12.14、SQLite 3.53.1、Linuxの検証では、Connection.backup()候補を読み取り専用で開き、昇格後のパスも別工程で再オープンしました。これにより成功フラグだけでは分からないパス誤りを検出できます。
3. 構造・外部キー・アプリ検査を組み合わせる
PRAGMA quick_checkがokの1行だけを返すことを要求し、時間が許せばより完全なintegrity_checkを使います。ただし外部キーは別です。SQLiteはintegrity_checkが外部キー違反を検出しないと明記しているため、foreign_key_checkも実行して空結果を要求します。
さらにuser_versionや移行表、必須テーブル・索引、既知のセンチネル、重要な件数・不変条件、代表的な読み取りを確認します。4行の検証ではquick_check=ok、外部キー行なし、sentinel=(sentinel, 3)となり、先頭16バイトを壊したコピーは再オープンで拒否されました。
4. 先に昇格し、旧版の削除は最後
検査が失敗した候補はローテーション集合に入れず、直前の正常バックアップと失敗した問い合わせを保持します。候補をその場で修復して合格扱いにせず、新しいスナップショットを作ります。構造検査の成功も業務データの意味的な完全性を保証しません。
すべて通過後、同一ファイルシステムでrenameまたはreplaceします。Pythonのos.replaceは成功時に原子的ですが、ファイルシステムをまたぐと失敗し得ます。旧版を削除する前に昇格パスを再オープンして最小センチネル検査を繰り返し、別途隔離環境で完全復元訓練を行います。
