素のPHPでDB変更を安全に戻すための三層設計

しろまえ殿は、フレームワークに頼らない小規模アプリでも、DB変更を戻せる仕組みを先に作った。 要点は、変更ファイル・適用記録・トランザクションを別々の安全網にすることだ。

変更を戻せる形にする

最初に、各変更を updown の組で管理した。 up は前へ進め、down は直前の状態へ戻す。戻す手順を書けない変更は、適用前に危険性を検討できる。

次に schema_migrations という記録テーブルを用意した。これは、どの版まで適用したかをDB自身に残す台帳である。

最後に、適用処理をトランザクションで囲んだ。トランザクションとは、複数の操作を一まとまりとして確定し、途中で失敗したら元へ戻す仕組みだ。

たとえば引っ越しで、荷物を半分だけ新居へ運んで作業を終えないのと同じだ。搬出と搬入の境目を管理し、失敗時に中途半端な状態を残さない。

実装時に分けたもの

SQLiteでは、PRAGMA journal_mode=WAL のようにトランザクション内で期待どおりに切り替わらない設定があることを確認した。 WALは読み書きを並行しやすくするSQLiteの記録方式だが、モード変更そのものはDDL変更と同じ扱いにできない。

そこで、モード設定はトランザクションの外で行い、テーブル変更だけをトランザクション内で処理した。 似た名前の操作を一つの塊に押し込まず、確定単位の違いで分けることが重要だった。

また、CREATE TABLE IF NOT EXISTS のような冪等な記述も使った。冪等とは、同じ操作を複数回行っても結果が壊れない性質である。

デプロイ順序を固定する

新しいカラムを読むコードを先に出すと、まだカラムが存在しないDBで失敗する。 しろまえ殿は、先にマイグレーションを適用し、その後でコードをデプロイする順序を採用した。

この順序なら、旧コードは追加されたカラムを使わずに動ける。切り替えの途中も、旧状態へ戻しやすい。

ただし、大きなテーブルの ALTER TABLE は長いロックを生むことがある。 書き込みを止められない規模になったら、新しいテーブルへ段階的に移して切り替える方式を検討する。

小さなアプリでは、三層の安全網だけでも判断材料が増える。 しろまえ殿の設計で重要だったのは、DBを変更することより、失敗したときに戻る道を先に用意したことだった。