Go製マイグレーションツールのsql-migrateを一通り試した

## 概要
Go製のマイグレーションツールである[sql-migrate](https://github.com/rubenv/sql-migrate)に入門したのでまとめていきます。
**複数のマイグレーションファイルを使用した時の動作**や、**複数の実行環境を分ける動作**も検証を行ったので参考になればと思います。
最後に、sql-migrateを選んだ理由についても記述しましたので、興味がありましたらご覧ください。

## 実行環境

```shell:terminal
$ go version
go version go1.15.4 darwin/amd64

$ docker -v
Docker version 20.10.6
$ docker-compose -version
docker-compose version 1.29.1
```

## 試してみる
最終的には以下のディレクトリ構成になりました。

```
$ tree
.
├── Dockerfile
├── dbconfig.yml
├── docker-compose.yml
├── migrations
│   ├── 20210704224341-create_users.sql
│   ├── 20210704230246-add_name_to_users.sql
│   └── 20210704231016-add_age_to_users.sql
└── tmp
```

### sql-migrateのインストール

```shell:terminal
$ go get -v github.com/rubenv/sql-migrate/...
```

### Dockerで環境を作る
Dockerを利用してMySQLの実行環境を整えます。

```docker-compose.yml
 version: "3"
 services:
   db:
     image: mysql:5.7
     ports:
       - "3306:3306"
     environment:
       MYSQL_ROOT_USER: root
       MYSQL_ROOT_PASSWORD: mysql
```

### データベースを作る
コンテナの中に入ってDBを作成します。環境別での動作も検証したいので、2つDBを作成します。

```shell:terminal
$ docker-compose up -d
$ docker ps
 CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
 ba6dc3787946   mysql:5.7   "docker-entrypoint.s…"   6 seconds ago   Up 2 seconds   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   sql-migrate_db_1
$ docker exec -it sql-migrate_db_1 bash
root@29e7d071a9fc:/# mysql -u root -p --host 127.0.0.1
mysql> create database sql_migrate_dev;
mysql> create database sql_migrate_stg;
```

### 設定ファイルを書く

```dbconfig.yml
 development:
   dialect: mysql
   datasource: root:mysql@tcp(127.0.0.1)/sql_migrate_dev?parseTime=true
   dir: ./migrations/ 
 staging:
   dialect: mysql
   datasource: root:mysql@tcp(127.0.0.1)/sql_migrate_stg?parseTime=true
   dir: ./migrations/
```

これで使用する準備は完了です。

### マイグレーションファイルを作成する
マイグレーションファイルは`sql-migrate new`で作成します。

```shell:terminal
 $ sql-migrate new create_users
 Created migration migrations/20210704224341-create_users.sql
```

作成したマイグレーションファイルの中身は以下の内容に変更します。

```sql:migrations/20210704224341-create_users.sql
 -- +migrate Up
 CREATE TABLE IF NOT EXISTS users (id int);
 
 -- +migrate Down
 DROP TABLE IF EXISTS users;
```

### マイグレーションを実行する
マイグレーションを実行するには`sql-migrate up`を使用します。
使用できるすべてのマイグレーションファイルを読み込みます。

```shell:terminal
 $ sql-migrate up 
 Applied 1 migration
```

### statusを確認する
statusを確認するには、`sql-migrate status`を使用します。

```shell:terminal
 $ sql-migrate status
 +---------------------------------+-------------------------------+
 |            MIGRATION            |            APPLIED            |
 +---------------------------------+-------------------------------+
 | 20210704224341-create_users.sql | 2021-07-04 13:57:40 +0000 UTC |
 +---------------------------------+-------------------------------+
```

usersテーブルが作成されていることが確認出来ます。

```shell:terminal
 mysql> use sql_migrate_dev
 
 Database changed
 mysql> show tables;
 +---------------------------+
 | Tables_in_sql_migrate_dev |
 +---------------------------+
 | gorp_migrations           |
 | users                     |
 +---------------------------+
 2 rows in set (0.00 sec)
```

### 1つバージョンを下げる
`sql-migrate down`を使用することでバージョンを下げることが出来ます。
downは1回ごとに1バージョン下がります。

```shell:terminal
 $ sql-migrate down
 Applied 1 migration
	$ sql-migrate status
 +---------------------------------+---------+
 |            MIGRATION            | APPLIED |
 +---------------------------------+---------+
 | 20210704224341-create_users.sql | no      |
 +---------------------------------+---------+
```

テーブルが無くなりました。

```shell:terminal
 mysql> show tables;
 +---------------------------+
 | Tables_in_sql_migrate_dev |
 +---------------------------+
 | gorp_migrations           |
 +---------------------------+
 1 row in set (0.00 sec)
```

### 3つのマイグレーションファイルで試してみる
新たに2つマイグレーションファイルを作成します。

```shell:terminal
 $ sql-migrate new add_name_to_users
 Created migration migrations/20210704230246-add_name_to_users.sql
 $ sql-migrate new add_age_to_users
 Created migration migrations/20210704231016-add_age_to_users.sql
```

```sql:migrations/20210704230246-add_name_to_users.sq
 -- +migrate Up
 ALTER TABLE users ADD COLUMN name varchar(10);
 
 -- +migrate Down
 ALTER TABLE users DROP COLUMN name;
```

```sql:migrations/20210704231016-add_age_to_users.sq
 -- +migrate Up
 ALTER TABLE users ADD COLUMN age int;
 
 -- +migrate Down
 ALTER TABLE users DROP COLUMN age;
```

一度の実行で、すべてマイグレーションファイルが読み込めていることが確認出来ます。

```
 $ sql-migrate up 
 Applied 3 migrations
 $ sql-migrate status
 +--------------------------------------+-------------------------------+
 |              MIGRATION               |            APPLIED            |
 +--------------------------------------+-------------------------------+
 | 20210704224341-create_users.sql      | 2021-07-04 14:11:40 +0000 UTC |
 | 20210704230246-add_name_to_users.sql | 2021-07-04 14:11:40 +0000 UTC |
 | 20210704231016-add_age_to_users.sql  | 2021-07-04 14:11:40 +0000 UTC |
 +--------------------------------------+-------------------------------+
```

新たに、nameとageが作成されました。

```shell:terminal
 mysql> show columns from users;
 +-------+-------------+------+-----+---------+-------+
 | Field | Type        | Null | Key | Default | Extra |
 +-------+-------------+------+-----+---------+-------+
 | id    | int(11)     | YES  |     | NULL    |       |
 | name  | varchar(10) | YES  |     | NULL    |       |
 | age   | int(11)     | YES  |     | NULL    |       |
 +-------+-------------+------+-----+---------+-------+
 3 rows in set (0.00 sec)
```

### 指定したバージョン数切り戻す
limitオプションを使用することで、複数バージョン切り戻すことが出来ます。

```shell:terminal
 $ sql-migrate down -limit=2
 Applied 2 migrations
 $ sql-migrate status
 +--------------------------------------+-------------------------------+
 |              MIGRATION               |            APPLIED            |
 +--------------------------------------+-------------------------------+
 | 20210704224341-create_users.sql      | 2021-07-04 14:11:40 +0000 UTC |
 | 20210704230246-add_name_to_users.sql | no                            |
 | 20210704231016-add_age_to_users.sql  | no                            |
 +--------------------------------------+-------------------------------+
```

### 別環境で実行してみる
envオプションを使用することで、実行環境を分けることが出来ます。envオプションを指定しない場合は、developmentの設定内容が使用されます。

```shell:terminal
 $ sql-migrate up -env="staging"
 Applied 3 migrations
 
 $ sql-migrate status -env="staging"
 +--------------------------------------+-------------------------------+
 |              MIGRATION               |            APPLIED            |
 +--------------------------------------+-------------------------------+
 | 20210704224341-create_users.sql      | 2021-07-04 14:20:13 +0000 UTC |
 | 20210704230246-add_name_to_users.sql | 2021-07-04 14:20:13 +0000 UTC |
 | 20210704231016-add_age_to_users.sql  | 2021-07-04 14:20:13 +0000 UTC |
 +--------------------------------------+-------------------------------+
```

```shell:terminal
 mysql> use sql_migrate_stg
 
 Database changed
 mysql> show columns from users;
 +-------+-------------+------+-----+---------+-------+
 | Field | Type        | Null | Key | Default | Extra |
 +-------+-------------+------+-----+---------+-------+
 | id    | int(11)     | YES  |     | NULL    |       |
 | name  | varchar(10) | YES  |     | NULL    |       |
 | age   | int(11)     | YES  |     | NULL    |       |
 +-------+-------------+------+-----+---------+-------+
 3 rows in set (0.00 sec)
```

これで、一通りの機能は網羅できたと思います。

## [番外編] なぜsql-migrateを選んだか
Go製のマイグレーションツールには、[golang-migrate/migrate](https://github.com/golang-migrate/migrate)や[goose](https://github.com/pressly/goose)、[GORM](https://gorm.io/index.html)といった複数の選択肢があります。最初に、その中で[sql-migrate](https://github.com/rubenv/sql-migrate)を選んだ理由をお話できればと思います。

まず、利用するマイグレーションツールには以下の条件を求めていました。

- CLIが利用できること
- upとdownができること
- statusなどが見やすく表示されること
- 2021年時点でアップデートされていること

これらの条件を加味した結果、残ったのが以下の2つです。

- [goose](https://github.com/pressly/goose)
- [sql-migrate](https://github.com/rubenv/sql-migrate)

次に、それぞれのメリットを調査しました。

#### sql-migrateを使うメリット
sql-migrateでは設定ファイルを利用することが出来、実行環境を分けることが容易です。
一方で、gooseでは設定ファイルの利用が出来ません。
(厳密には、現在更新されている[GitHub版](https://github.com/pressly/goose)では利用できず、更新が止まっている[Bitbucket版](https://bitbucket.org/liamstask/goose/src/master/)では利用可能なようです。)

#### gooseを使うメリット
gooseは、特定のバージョンを指定してmigrate出来ます。

```
$ goose up-to 20170506082420
```

一方で、sql-migrateは`-limit`オプションを使用することで複数バージョンdownしたりup出来ます。

```
$ sql-migrate down -limit=2
```

人にもよるとは思いますが、個人的にはバージョン指定出来る方が便利だなと感じています。

#### 結局、sql-migrateを選んだ
設定ファイルが無いとめんどくさいよね、ということでsql-migrateを選びました。

なお、選定には以下の記事を大いに参考にさせていただきました。
[結局 go の migration ツールは何を使えばよいのか](https://onemuri.space/note/Is3eV1-d1/)

以上です。

## 参考
[結局 go の migration ツールは何を使えばよいのか](https://onemuri.space/note/Is3eV1-d1/)