forward-onlyとは「復旧不能」ではない
Cloudflare D1の公式migration機構は、SQL fileのcreate・list・applyを管理します。
Wranglerのmigration CLIにdown commandはありません。
適用済みfile名はd1_migrationsへ記録され、次回以降のapply対象から外れます。
一方、D1 Time Travelには指定時点へdatabase全体を戻す機能があります。
つまり、schema変更を取り消す専用down migrationはなくても、database全体を過去の状態へrestoreする手段はあります。
この2つを混同すると、「復旧不能」と誤解するか、反対に小さなschema修正へ破壊的な全体restoreを使ってしまいます。
D1を事前確認とbookmarkからexpand、migrate、検証後のcontractへ進める安全なmigration手順*図: 互換性を保ってexpandとmigrateを進め、検証後の別releaseでcontractする。重大障害時だけ事前bookmarkから全体restoreする。*
復旧手段を3種類に分ける
通常の第一選択は新しいforward migrationです。
既に受け付けたwriteを保持したまま、欠けたindex、trigger、column、dataを補正できます。
Worker codeだけに問題があり、現在のschemaと旧codeが互換なら、直前のWorker versionへ戻す選択肢もあります。
schemaを先に破壊しているとcodeだけ戻せないため、後述の互換期間が重要です。
D1 Time Travel restoreは、指定bookmark時点のdatabase全体で現在のdatabaseを上書きします。
実行中のqueryとtransactionはcancelされ、bookmark取得後のwriteを失います。
その損失を受け入れられる重大障害でのみ、writeを止め、対象databaseとbookmarkを再確認して使います。
restore完了時に返されるbookmarkは、restore自体を取り消すための証拠として保存します。
restore後はincident migrationを再applyしない
pre-migration bookmarkへrestoreすると、d1_migrationsもその時点へ戻ります。
事故を起こしたincident migrationはremote plan上で再びpendingになります。
restore直後はmigration automationを停止し、wrangler d1 migrations list ... --remoteをread-onlyで確認します。
事故migrationをblindに再applyしません。
restored schema、migration history、pending file、他environmentへの適用状況を固定し、review済みのrecovery/re-baseline planをlocal copyで検証します。
一般化した安全な再開commandはありません。
判断できない場合はwrite停止を維持してCloudflare Supportへ確認し、d1_migrationsの手動変更やremote down SQLで辻褄を合わせません。
migration historyを変更しない
d1_migrationsは、どのfileが適用済みかを示す運用証跡です。
このrepositoryでは、そのrowを手動で削除・変更しません。
既存migration fileも適用後には書き換えず、修正は新しい番号のfileへ追加します。
migrations/rollback/*.down.sqlは、本番へ流すdown migrationではありません。
破壊的変更の影響や失われるdataをreviewするための、local専用のreview/test fixtureです。
必要な変更にだけ用意し、local databaseで復旧形状を検証します。
本番手順の正本は、このsite repository内のmigrations/README.mdです。
migration fileは1回だけ適用される前提で書く
ALTER TABLE ... ADD COLUMNは、同じcolumnが存在する状態で再実行すれば失敗します。
D1 migrationは履歴で未適用fileを選び、各fileを1回だけ適用する仕組みです。
WHEREやNOT EXISTSでdata上書きを絞っても、file全体が何度でも安全に再実行できることにはなりません。
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 = '';
remoteへ進む前にlocal D1へapplyし、schema、主要query、row count、backfill結果をtestします。
適用失敗時はWranglerがそのmigrationをrollbackしますが、それ以前に成功済みのmigrationまで戻るわけではありません。
expand → migrate → contractをreleaseで分ける
安全なschema変更は1回のdeployへ詰め込みません。
- expand: old codeが無視できるcolumn・table・indexを追加する。
- migrate: new codeをdual-read/dual-write対応でdeployし、dataをbackfillする。
- verify: old/new path、row count、null、index、主要queryを観測する。
- contract: 十分な互換期間後の別migrationでold columnやold triggerを除く。
Workerのversion切替とD1 applyは同時には完了しません。
expand時点ではold Workerが動き、migrate時点ではnew Workerがbackfill前のrowも読める必要があります。
「どちらを先に実行しても必ず安全」と決めつけず、各releaseで許容するversion/schema組合せを明記します。
remote apply直前にbookmarkを取る
このrepositoryの手順は次の順です。
remote commandは対象・未適用file・明示承認を確認してから実行します。
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
全体restoreが必要なら、影響範囲を確認したうえで次の形を使います。
これは通常のrollback commandではありません。
bash
pnpm exec wrangler d1 time-travel restore kirin-ai-blog-db \
--bookmark "$PRE_MIGRATION_BOOKMARK"
exportとapplication backupは別物
wrangler d1 exportは便利ですが、Cloudflare公式docsではvirtual tableを含むdatabaseのexportをsupportしません。
このsiteはFTS5 virtual tableを使うため、exportを本番database全体の主要復旧手段にはしません。
またexport中はqueryがblockされるため、実行時間も考慮が必要です。
管理画面のbackupは記事とmarketing設定を移すapplication data snapshotであり、comment、like、rate-limit、migration historyを含むD1 full backupではありません。
production database全体の時点復旧はTime Travel、最新writeを残す補正はforward migration、application contentの移送は管理画面backup、と目的を分けます。
障害時の判断順
- apply自体が失敗した: errorを確認し、新しいforward migrationまたは未適用fileの修正をlocalで検証する。履歴は触らない。
- apply済みで最新writeを保持したい: appを互換versionへ戻すか、新しいforward migrationで補正する。
- database全体が壊れ、bookmark後のwriteを失ってよい: writeを止め、Time Travel restoreを実行する。
- contract後にold codeへ戻したい: old codeが現schemaへ対応しないならcode rollbackを中止し、forward fixを優先する。
公式参考資料