跳轉到

golang-migrate Transaction Safety Contract

概念概覽

核心衝突

核心知識

核心衝突

golang-migrate 預設對每個 .sql 檔案包裝一個 transaction。而 CREATE INDEX CONCURRENTLYDROP INDEX CONCURRENTLY 在 PostgreSQL 中**明確禁止在 transaction 內執行**,執行後會造成 migration dirty state,且難以自動回復。

常見誤區

  • 開發者誤以為加上 CONCURRENTLY 可以做 online DDL,但在 golang-migrate 環境下這是**虛假能力** — 指令根本無法執行成功。
  • 對小型 admin/audit table,標準 CREATE INDEX(短暫 lock)是正確選擇。

正確修法(不只修眼前這個)

掃描整個 migrations/postgres/ 目錄找所有違規 DDL,而不是只修觸發問題的那個檔案(案例中 032 和 042 都有問題)。

CI 防線建立

// test/migrations/migration_lint_test.go
// 用 Go test 實作,比 grep shell script 更穩定:
// - 能跳過 comment 內的 CONCURRENTLY 關鍵字
// - 能處理 edge case(大小寫、內嵌字串等)

Migration Runner 雙軌問題

若外部 migrate/migrate CLI 寫 schema_migrations 表,而 cmd/twirpserver --migrategame_schema_migrations 表,兩者互不感知,會導致 dirty state 診斷困難。統一 runner 的做法:docker-compose.yml 的 migrate service 改用同一個 twirpserver image + --migrate flag。

經驗教訓

  • CONCURRENTLY DDL 在 golang-migrate 下是虛假能力,應禁止使用

  • 發現一個違規 migration 時,要全掃目錄而非只修單一檔案

  • Migration lint 用 Go test 實作比 grep shell script 更可靠

  • 雙 schema_migrations 表是 dirty state 難以診斷的根因

常見陷阱

  • 只修觸發問題的 migration,沒有掃描歷史檔案

  • 以為加 CONCURRENTLY 就能 online DDL,忽略 golang-migrate 的 transaction 包裝

  • 用 grep shell script 做 migration lint,遇到 comment 或大小寫時誤判

最佳實踐

  • repo 內所有 migrations/*.sql 禁止 CONCURRENTLY DDL,用 CI test 強制執行

  • docker-compose 的 migrate service 與 production migrate runner 使用同一 image

  • bootstrap test 要同時驗 fresh DB + upgrade path 兩種場景

相關概念

相關視角

以下頁面與本概念共享主題,但從不同角度切入。保留獨立視角同時提供交叉參考:

來源 Sessions

日期 Session 貢獻摘要

| 2026-03-30 | 45cbfb38-30e4-4de0-9d9c-1ea02a62e945 | 揭示 CREATE INDEX CONCURRENTLY 在 golang-migrate transaction 架構下的根本衝突,並建立完整的 migration 安全契約(lint CI 防線) |


本概念頁面由 Semi-Brain Wiki 系統自動維護

最後更新: 2026-03-30