APIを開発する際、データストアとしてMySQLやPostgresSQLなどのRDB(リレーショナルデータベース)を使うことは多いかと思います。

フレームワークによってはRDBのスキーマを編集するのに便利なマイグレーションツールが含まれていることもありますし、私自身アプリケーション開発についてはRuby on Railsから入ったのであまり深く意識せず与えられたものを使ってきました。

しかし、様々な技術スタックで開発をするような状況ではRailsのような感覚のマイグレーションツールは使えないことも少なくはないと思います。

ここではGAE/GoとデータストアとしてPostgreSQLを使う場合のデータベースマイグレーションの運用方法について考えてみました。


データベースマイグレーションの運用にあたって以下の点を考慮し、ツールの選定やセットアップを行いました。

  • マイグレーションに必要となる基本的なオペレーションが簡単であること
  • マイグレーションの記述方法はこだわらない
  • デプロイ時に自動的にスキーマを反映するようにしたい

ということで使うことにしたのが以下のツールです。

sql-migrateの使い方はREADMEにもありますし、日本語でも紹介する記事はあるので詳しくは書きませんが、私は Makefile にマイグレーションのためのタスクを作ってマイグレーションの前後に実行したいコマンドなどをまとめるようにしています(このあたりは別の記事で書こうと思います)。


マイグレーションの手順

ローカル環境でマイグレーション

sql-migrateの使い方はシンプルなので特に解説するほどでもないのですが、基本はローカル環境で以下のような流れで実行していきます。

  • new: 新しいマイグレーションファイルを生成する
  • up: マイグレーションを適用する
  • down: マイグレーションのロールバック

正しくマイグレーションファイルを書いていれば sql-migrate up した時点でスキーマが更新されているはずです。

デプロイ時にマイグレーションを適用する

ローカル環境でマイグレーションを適用した後はどうすればいいでしょうか。
例としてGAE/GoのサーバーからCloud SQLにたてたPostgreSQLを使うケースを考えてみます。

これもREADMEに記述されている内容ではありますが、実例としてコードの一部を抜粋して紹介します。

FileMigrationSource でマイグレーションファイルの場所を指定します、私は app/db/migrations 以下で管理しているので Dir でそのように指定しています。
(データベースに接続するために sqlx を使っています)

package db

import (
	"github.com/jmoiron/sqlx"
	migrate "github.com/rubenv/sql-migrate"
)

var (
	migrations = &migrate.FileMigrationSource{
		Dir: "app/db/migrations",
	}
)

func ExecMigrations(postgresURL string) error {
	pg, err := sqlx.Connect("postgres", postgresURL)
	if err != nil {
		return err
	}
	defer pg.Close()

	appliedCount, err := migrate.Exec(pg.DB, sqlDriver, migrations, migrate.Up)
	if err != nil {
		return err
	}
	log.Printf("Applied %v migrations", appliedCount)
	return nil
}

ExecMigration はマイグレーションを適用するための関数で、これをmainで呼ぶことで起動時にマイグレーションが実行されます。
マイグレーションを実行するかどうかは環境変数などで制御すると良いかと思います。

package main

func main() {
	// 起動時にマイグレーションを適用するかどうかのフラグ
	if applyMigration == True {
		err := db.ExecMigrations(postgresURL)
		if err != nil {
			log.Fatal(err)
		}
	}
	// 略
}

やっていることは少ないですが、これだけでGAEへのデプロイと同時にPostgreSQLのスキーマを更新できるようになりました。
正常にマイグレーションが実行されていれば、ログに「Applied x migrations」と表示されているはずです。

sql-migrateはツールとしてシンプルで使いやすくGolangでしか使えないわけではないので、他の技術スタックで開発する際にも使えそうです。