Gorm/Gormigrateを利用したMigrationについて
この記事は、株式会社アトラエのアドベントカレンダー2021年18日目の記事です。
株式会社アトラエのエンジニアの@takayです!
現在シニア向けマッチングサービスInowのバックエンドエンジニアをしております。InowサービスのmainのバックエンドサービスはGo言語を採用しており、Gorm/Gormigrateのライブラリーを利用したMigrationを実装したときの話を下記で記載していきます。
Gormigrateについて
Migrationを実装するにあたって要件はテーブルの追加/削除・カラムの追加/変更/削除・indexの追加削除等々当然ありますが、InowではORMとしてGormを利用しており基本的にはGormのMigrationの機能を利用しようと考えていました。
schemaのversionningやrollback等の機能も運用を考慮すると必要と考えていましたが、Gormのライブラリーではそのような機能が提供されていないため、色々と調べた結果GormigrateというライブラリーがGormのmigrationのhelper的な機能を提供しており利用をすることとしました
実装について
GormigrateのReadmeに以下のように書かれています
package main
import (
"log"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
func main() {
db, err := gorm.Open("sqlite3", "mydb.sqlite3")
if err != nil {
log.Fatal(err)
}
m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{
{
ID: "201608301400",
Migrate: func(tx *gorm.DB) error {
type Hoge struct {
gorm.Model
Name string
}
return tx.AutoMigrate(&Hoge{})
},
Rollback: func(tx *gorm.DB) error {
return tx.Migrator().DropTable("hoges")
},
},
})
if err = m.Migrate(); err != nil {
log.Fatalf("Could not migrate: %v", err)
}
log.Printf("Migration did run successfully")
}
Optionsでversioningに必要なテーブルの名前が指定できたり(defaultではmigrationsというテーブル)、migrationIdのカラムの名前/長さなどが指定できます。
特定のversionまでMigrationしたい場合にはMigrateToというメソッドが用意されています。
func (g *Gormigrate) MigrateTo(migrationID string) error {
if err := g.checkIDExist(migrationID); err != nil {
return err
}
return g.migrate(migrationID)
}
直前のMigrationをrollbackさせたい場合にはRollbackLastというメソッドがあります。
// RollbackLast undo the last migration
func (g *Gormigrate) RollbackLast() error {
if len(g.migrations) == 0 {
return ErrNoMigrationDefined
}
g.begin()
defer g.rollback()
lastRunMigration, err := g.getLastRunMigration()
if err != nil {
return err
}
if err := g.rollbackMigration(lastRunMigration); err != nil {
return err
}
return g.commit()
}
直前の特定のrollbackさせたい場合にはRollbackLastというメソッドを利用します。
// RollbackTo undoes migrations up to the given migration that matches the `migrationID`.
// Migration with the matching `migrationID` is not rolled back.
func (g *Gormigrate) RollbackTo(migrationID string) error {
if len(g.migrations) == 0 {
return ErrNoMigrationDefined
}
if err := g.checkIDExist(migrationID); err != nil {
return err
}
g.begin()
defer g.rollback()
for i := len(g.migrations) - 1; i >= 0; i-- {
migration := g.migrations[i]
if migration.ID == migrationID {
break
}
migrationRan, err := g.migrationRan(migration)
if err != nil {
return err
}
if migrationRan {
if err := g.rollbackMigration(migration); err != nil {
return err
}
}
}
return g.commit()
}
その他
Gormigrateにはmigration用のファイルを自動で生成するみたいな機能(railsで言うとrails generate migration AddFugoToHogesで作られるファイルやつ)は無いので、こちらはgo generate等を使ってファイルを自動生成できるようにしました。この実装についてはまた別の機会にでも書こうと思います。
Discussion