0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

個人的備忘録:Gorm の AutoMigrate() は何度実行しても問題ないのか?疑問に感じたため、自分なりに調べてみた

Posted at

はじめに

データベースのスキーマ管理は、アプリケーション開発において重要なポイントです。

個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。

特に AutoMigrate() のような自動マイグレーション機能を使う場合、「何度も実行しても問題ないのか?」と疑問に思うことがあります。

記事を書こうと思ったきっかけ

Gorm の AutoMigrate() を使用したマイグレーション処理に関して、「何度も実行しても問題ないのか?」という疑問を持つ人が多いため、

その動作や安全な運用方法についてまとめることにしました。

結論: dbConn.AutoMigrate() の動作による

現在のコードは Gorm の AutoMigrate() を使ってマイグレーションを実行しているため、「すでに適用済みのマイグレーションはスキップされる」 仕様になっており、基本的に何度実行しても問題ありません。

補足:現在のコードは、以下になります

dbConn.AutoMigrate() の動作

Gorm の AutoMigrate() は以下のように動作します。

  • テーブルが存在しない場合、新規作成する
  • カラムが足りない場合、追加する
  • 既存のカラムのデータ型や制約の変更はしない
  • 既存のカラムやテーブルの削除はしない

つまり、AutoMigrate()破壊的な変更(カラム削除・変更)をしないため、何度実行しても問題が発生することは少ないです。

問題になる可能性があるケース

以下のような場合は、毎回の実行で問題が発生する可能性があります。

1. AutoMigrate() 以外に DROP TABLEALTER TABLE を実行している場合

AutoMigrate() だけなら安全ですが、明示的に DROP TABLEALTER TABLE を使っている場合、データが消えたり、不整合が発生する可能性があります。

2. AutoMigrate() に依存せず、手動で SQL を実行している場合

以下のようなコードがあると、何度も実行するとエラーや不整合が発生する可能性があります。

// すでにテーブルが存在するとエラーになる
 dbConn.Exec("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255));")

3. AutoMigrate() の前に DROP TABLE を実行している場合

dbConn.Exec("DROP TABLE IF EXISTS users;") // データが消える
dbConn.AutoMigrate(&model.User{})

このようなコードがある場合、データが毎回削除されるので要注意です。

安全にするための対策

方法 1: AutoMigrate() で対応

現在のコードのままで AutoMigrate() を使うなら基本的に問題ありませんが、Gorm の AutoMigrate() の仕様を理解し、破壊的な変更をしないことを確認する必要があります。

err = dbConn.AutoMigrate(
    &model.User{},
    &model.Category{},
    &model.Question{},
    &model.Answer{},
    &model.Choice{},
    &model.Score{},
)

方法 2: マイグレーションの実行を1回だけにする

migrate.lock ファイルを使い、一度だけマイグレーションを実行するようにする

package main

import (
    "fmt"
    "log"
    "os"
    "skill-typing-back/db"
    "skill-typing-back/model"
)

func main() {
    if _, err := os.Stat("/app/migrate.lock"); err == nil {
        fmt.Println("Migrations already applied, skipping.")
        return
    }

    dbConn := db.NewDB()
    if dbConn == nil {
        log.Fatal("データベースの初期化に失敗しました")
    }
    log.Println("データベースの初期化に成功")

    sqlDB, err := dbConn.DB()
    if err != nil {
        log.Fatal("Failed to get DB connection:", err)
    }
    defer sqlDB.Close()

    fmt.Println("Starting migration...")
    err = dbConn.AutoMigrate(
        &model.User{},
        &model.Category{},
        &model.Question{},
        &model.Answer{},
        &model.Choice{},
        &model.Score{},
    )
    if err != nil {
        log.Fatal("Migration failed:", err)
    }

    f, err := os.Create("/app/migrate.lock")
    if err != nil {
        log.Fatal("Failed to create migrate.lock:", err)
    }
    defer f.Close()

    fmt.Println("Successfully Migrated")
}

まとめ

AutoMigrate() は基本的に何度実行しても問題なく、idempotent であるため、特に気にせず使える点は便利だと感じました。

ただし、より安全に運用するなら migrate.lock を活用して、一度だけ実行する仕組みにするのがベスト だと思います。

個人的には、まず migrate.lock を使う方法がシンプルで実装しやすく、かつ安全なので、この方法を試してみるのが良さそう だと感じました!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?