コンテンツにスキップ

データベースマイグレーション

ひとつのアプリケーションが相手にするデータベースは、ひとつではありません。自分の 手元のファイル、同僚の手元のファイル、CI の実行ごとに作られる使い捨てのもの、 ステージング、本番 —— そのすべてを、誰も5か所で手作業の ALTER TABLE を打たずに 同じスキーマへ揃えなければなりません。

マイグレーションは、番号の付いた再実行可能なスキーマ変更の1ステップです。同じ 順序の集合をどのデータベースに再生しても同じ構造にたどり着きます。だからスキーマは、 各環境がそれぞれ覚えているものではなく、リポジトリが持つものになります。

用語 意味
マイグレーション 前進の変更と、その巻き戻しを1つにしたファイル
up / down 前進の方向と、ロールバックの方向
バージョン 集合の順序を決める、マイグレーションの番号
適用済み / 未適用 そのデータベースに記録済みか、まだ待っているか

適用済みのバージョンはデータベース自身に、番号で記録されます。pw migrate up を 2回実行しても安全なのはこの記録があるからです。2回目は未適用のものを見つけません。

マイグレーションは migrations/ に置き、goose の形式を使います。1つの .sql ファイルに、注釈で区切られた2つのセクションを書きます。

-- +goose Up
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
-- +goose Down
DROP TABLE users;
Terminal window
pw migrate create add_email

実行するつもりがなくても Down は書いておきます。実行できないロールバックは、 失敗したデプロイの障害時間を、逆向きのマイグレーションを書き終えるまで引き延ばします。

pw dev は起動時に未適用のマイグレーションを適用するので、 普段の内側のループは「ファイルを作り、編集し、再起動する」だけです。開発中に migrate コマンドを打つ場面はほとんどありません。

もう半分がデプロイとロールバックで、こちらは明示的に行います。

Terminal window
pw migrate status
pw migrate up
pw migrate down

自分で作ったのではないデータベースに対しては、up の前の status はその数秒を かける価値があります。アクションの一覧は pw migrate に あります。

どのデータベースに適用されるか

Section titled “どのデータベースに適用されるか”

マイグレーションは middleware.rdb.write_group(設定されていれば、より狭い middleware.rdb.migration_group)へ向かいます。readonly の接続が選ばれることは なく、それをマイグレーション先に設定すると、最初の ALTER TABLE ではなく起動時に 失敗します。

pw は設定の優先順位を再実装せず、アプリケーション自身に解決済みの DSN を尋ねます。 つまりマイグレーションは APP_ENV の選択に従います。開発用でないデータベースへ 向けるときは、環境を確認してください。接続グループはリレーショナルデータベース、 キーそのものはアプリケーション設定一覧にあります。

testutil.WithMigrations("../migrations") はテストサーバの起動前に集合を適用します。 スキーマの届き方は DSN のエンジンによって変わります。

  • SQLite はキャッシュしたスナップショットをコピー先のデータベースへ再生します。 sqlite://:memory: が動くのはこのためです。プロセス内のデータベースには DSN で 到達できないので、接続文字列ではなく SQL のほうを転送します。
  • PostgreSQL と MySQL はマイグレーションを直接適用します。同じデータベースに 対する2回目の TestRun は何も適用せずスキーマを再利用します。1つのパッケージの テスト群が、用意済みのサーバを共有できるのはこの挙動によります。

サーバ系の DSN は、テストスイート専用のデータベースへ向けてください。適用済み バージョンは番号で記録されるため、別プロジェクトのバージョン 1 をすでに持つ データベースでは、最初のマイグレーションが適用済みに見え、スキーマは永遠に届きません —— しかもエラーは出ません。テストを参照してください。

スキーマだけでは、動かす相手としては物足りないのが普通です。それを使えるものにする 行はシードデータにあります。