前進のみとは「復旧不能」ではない
Cloudflare D1の公式マイグレーション機構は、SQLファイルのcreate・list・適用を管理します。
Wranglerのmigration CLIにdown commandはありません。
適用済みファイル名はd1_migrationsへ記録され、次回以降の適用対象から外れます。
一方、D1 Time Travelには指定時点のdatabase全体(データベース全体)を戻す機能があります。
つまり、スキーマ変更を取り消す専用down マイグレーションはなくても、データベース全体を過去の状態へ復元する手段はあります。
この2つを混同すると、「復旧不能」と誤解するか、反対に小さなスキーマ修正へ破壊的な全体復元を使ってしまいます。
D1を事前確認とブックマークからexpand、移行、検証後の仕様へ進める安全なマイグレーション手順*図: 互換性を保ってexpandと移行を進め、検証後の別リリースで仕様する。重大障害時だけ事前ブックマークから全体復元する。*
復旧手段を3種類に分ける
通常の第一選択は新しいforward migration(前進マイグレーション)です。
既に受け付けた書き込みを保持したまま、欠けた索引、トリガー、column、データを補正できます。
Worker コードだけに問題があり、現在のスキーマと旧コードが互換なら、直前のWorker バージョンへ戻す選択肢もあります。
スキーマを先に破壊しているとコードだけ戻せないため、後述の互換期間が重要です。
D1 Time Travel 復元は、指定ブックマーク時点のデータベース全体で現在のデータベースを上書きします。
実行中のクエリとtransactionはcancelされ、bookmark取得後のwrite(書き込み)を失います。
その損失を受け入れられる重大障害でのみ、書き込みを止め、対象データベースとブックマークを再確認して使います。
復元完了時に返されるブックマークは、復元自体を取り消すための証拠として保存します。
復元後はincident マイグレーションを再適用しない
pre-migration bookmark(事前マイグレーションのブックマーク)へ復元すると、d1_migrationsもその時点へ戻ります。
事故を起こしたincident migration(事故マイグレーション)はリモート plan上で再びpendingになります。
restore直後はmigration automationを停止し、マイグレーション自動化の再開条件を確認してwrangler d1 migrations list ... --remoteをread-onlyで確認します。
事故migrationをblindに再applyしません(事故マイグレーションを再適用しません)。
restored スキーマ、マイグレーション 履歴、pending ファイル、他environmentへの適用状況を固定し、レビュー済みのrecovery/re-baseline planをローカル コピーで検証します。
一般化した安全な再開コマンドはありません。
判断できない場合は書き込み停止を維持してCloudflare Supportへ確認し、d1_migrationsの手動変更やリモート down SQLで辻褄を合わせません。
マイグレーション 履歴を変更しない
d1_migrationsは、どのファイルが適用済みかを示す運用証跡です。
このリポジトリでは、そのrowを手動で削除・変更しません。
既存マイグレーション ファイルも適用後には書き換えず、修正は新しい番号のファイルへ追加します。
migrations/rollback/*.down.sqlは、本番へ流すdown マイグレーションではありません。
破壊的変更の影響や失われるデータをレビューするための、local専用のreview/test fixture(ローカル専用のレビュー/テスト用fixture)です。
必要な変更にだけ用意し、ローカル データベースで復旧形状を検証します。
本番手順の正本は、このサイト リポジトリ内のmigrations/README.mdです。
マイグレーション ファイルは1回だけ適用される前提で書く
ALTER TABLE ... ADD COLUMNは、同じcolumnが存在する状態で再実行すれば失敗します。
D1 マイグレーションは履歴で未適用ファイルを選び、各ファイルを1回だけ適用する仕組みです。
WHEREやNOT EXISTSでデータ上書きを絞っても、ファイル全体が何度でも安全に再実行できることにはなりません。
sql
-- expand: old codeが無視できるadditive change
ALTER TABLE posts ADD COLUMN title_en TEXT NOT NULL DEFAULT '';
-- migrate: 既存の翻訳を上書きしないbounded backfill
UPDATE posts
SET title_en = title
WHERE title_en = '';
リモートへ進む前にローカル D1へ適用し、スキーマ、主要クエリ、row count、バックフィル結果をテストします。
適用失敗時はWranglerがそのマイグレーションをrollbackしますが、それ以前に成功済みのマイグレーションまで戻るわけではありません。
expand → 移行 → 仕様をリリースで分ける
変更はexpand・migrate・contractの3段階に分け、安全なスキーマ変更を1回のデプロイへ詰め込みません。
- expand: old コードが無視できるcolumn・table・索引を追加する。
- 移行: new コードをdual-read/dual-書き込み対応でデプロイし、データをバックフィルする。
- verify: old/new パス、row count、null、索引、主要クエリを観測する。
- 仕様: 十分な互換期間後の別マイグレーションでold columnやold トリガーを除く。
Workerのバージョン切替とD1 適用は同時には完了しません。
expand時点ではold Workerが動き、移行時点ではnew Workerがバックフィル前のrowも読める必要があります。
「どちらを先に実行しても必ず安全」と決めつけず、各リリースで許容するバージョン/スキーマ組合せを明記します。
リモート 適用直前にブックマークを取る
このリポジトリの手順は次の順です。
リモート コマンドは対象・未適用ファイル・明示承認を確認してから実行します。
bash
# 1. local applyと対象test
pnpm run d1:migrate:local
# 2. remote planの確認
pnpm exec wrangler d1 migrations list kirin-ai-blog-db --remote
# 3. 承認後にwriteを止め、apply直前のbookmarkを保存
pnpm exec wrangler d1 time-travel info kirin-ai-blog-db --json
# 4. database名と未適用fileを再確認してapply
pnpm run d1:migrate:remote
全体復元が必要なら、影響範囲を確認したうえで次の形を使います。
これは通常のrollback コマンドではありません。
bash
pnpm exec wrangler d1 time-travel restore kirin-ai-blog-db \
--bookmark "$PRE_MIGRATION_BOOKMARK"
exportとapplication バックアップは別物
wrangler d1 exportは便利ですが、Cloudflare公式ドキュメントではvirtual tableを含むデータベースのexportをsupportしません。
このサイトはFTS5 virtual tableを使うため、exportを本番データベース全体の主要復旧手段にはしません。
またexport中はクエリがblockされるため、実行時間も考慮が必要です。
管理画面のバックアップは記事とmarketing設定を移すapplication データ snapshotであり、comment、like、rate-limit、マイグレーション 履歴を含むD1 full バックアップではありません。
本番 データベース全体の時点復旧はTime Travel、最新書き込みを残す補正はforward マイグレーション、application contentの移送は管理画面バックアップ、と目的を分けます。
障害時の判断順
- 適用自体が失敗した: エラーを確認し、新しいforward マイグレーションまたは未適用ファイルの修正をローカルで検証する。履歴は触らない。
- 適用済みで最新書き込みを保持したい: appを互換バージョンへ戻すか、新しいforward マイグレーションで補正する。
- データベース全体が壊れ、ブックマーク後の書き込みを失ってよい: 書き込みを止め、Time Travel 復元を実行する。
- 仕様後にold コードへ戻したい: old コードが現スキーマへ対応しないならコード rollbackを中止し、forward fixを優先する。
公式参考資料