1行INSERTしてcommitしたのに、コピー先は0行。しかも integrity_check は ok だった。SQLiteのバックアップは、ファイルが開けただけで成功扱いすると危ないんだよね。
自分は今回、WALモードで本体コピーと Connection.backup() を比べた。復元後の中身まで確認したい。
Q. commitした行は、どこへ消えた?
元のDBには残っている。確定した更新はWAL(Write-Ahead Log)側にあり、本体のコピーでは拾えなかった。
WALモードでは更新をログに書き、チェックポイントで本体へ反映する。今回は自動チェックポイントを無効にし、空のテーブルを本体へ反映してからINSERTする。接続も開いたまま。
失敗を再現する実験用の設定なので、運用DBへの適用は避けたい。
Q. 本当に壊れていないのに、データが足りない?
wal_backup.py として保存し、python3 wal_backup.py で実行。Python 3.14.6/SQLite 3.53.4で確認した。追加パッケージは不要。DBは終了時に削除される。
import shutil
import sqlite3
from contextlib import closing
from pathlib import Path
from tempfile import TemporaryDirectory
def inspect(path):
with closing(sqlite3.connect(path)) as db:
return (
db.execute("SELECT count(*) FROM jobs").fetchone()[0],
db.execute("PRAGMA integrity_check").fetchone()[0],
)
with TemporaryDirectory() as tmp:
root = Path(tmp)
live = root / "live.db"
with closing(sqlite3.connect(live)) as src:
assert src.execute("PRAGMA journal_mode=WAL").fetchone()[0] == "wal"
src.execute("PRAGMA wal_autocheckpoint=0")
src.execute("CREATE TABLE jobs(id INTEGER PRIMARY KEY)")
src.commit()
src.execute("PRAGMA wal_checkpoint(TRUNCATE)")
src.execute("INSERT INTO jobs VALUES (1)")
src.commit()
# 接続を開いたまま、確定済みの更新をWALに残す
shutil.copyfile(live, root / "copy.db")
with closing(sqlite3.connect(root / "backup.db")) as dst:
src.backup(dst)
copied = inspect(root / "copy.db")
backed = inspect(root / "backup.db")
print("file copy:", copied)
print("backup: ", backed)
assert copied == (0, "ok")
assert backed == (1, "ok")
実行結果はこうなった。括弧内は行数と検査結果。
file copy: (0, 'ok')
backup: (1, 'ok')
コピー先は空のテーブルとして整合している。integrity_check が通っても、期待した更新まで入っているとは限らない。この0行と ok の組み合わせは、地味に怖い。
Q. 何を変えれば、取りこぼしを防げる?
src.backup(dst) で接続経由のバックアップを作る。こちらはcommit済みの1行を含んでいた。保存先は閉じてから開き直して確認した。
本体と -wal の個別コピーは、途中で更新が進むと時点をそろえる問題が増える。今回は書き込みを止めて比較した。継続的な書き込み中の待ち時間や負荷は未測定。
ファイル名に日付が入っていると、つい安心してしまう。中身が0行でも名前は立派なんだよな。
Q. 復元確認は何を見ればいい?
assertは実験用。実務では必要なテーブルを読み、保存したい処理IDの存在まで確認する。件数が同じでも別の行が欠けるケースはある。
どの処理まで残れば合格かを先に決める。検証用の復元先で、その条件に沿って照合する。
おわりに
今回は0行と1行に分かれた。整合性検査は両方とも通るので、復元した内容も確認したい。まず残すべき処理IDを1つ照合する。履歴が増えても、この確認をバックアップ処理の完了条件に残しておく。
