DB の土台を追加(マイグレーション、待ち行列・番組登録の操作、embedded-postgres によるテスト)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"sort"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var migrations embed.FS
|
||||
|
||||
const migrationLockID int64 = 0x747665725f646231
|
||||
|
||||
// Migrate は同じ接続で排他ロックを保持し、未適用の SQL を順に実行する。
|
||||
func Migrate(ctx context.Context, pool *pgxpool.Pool) error {
|
||||
conn, err := pool.Acquire(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("migration connection: %w", err)
|
||||
}
|
||||
defer conn.Release()
|
||||
if _, err := conn.Exec(ctx, "SELECT pg_advisory_lock($1)", migrationLockID); err != nil {
|
||||
return fmt.Errorf("migration lock: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
// 接続を返す前にロックを解放する。解放に失敗した接続は閉じる。
|
||||
if _, err := conn.Exec(context.Background(), "SELECT pg_advisory_unlock($1)", migrationLockID); err != nil {
|
||||
conn.Conn().Close(context.Background())
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := conn.Exec(ctx, `CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version TEXT PRIMARY KEY,
|
||||
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
)`); err != nil {
|
||||
return fmt.Errorf("migration metadata: %w", err)
|
||||
}
|
||||
files, err := fs.Glob(migrations, "migrations/*.sql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Strings(files)
|
||||
for _, name := range files {
|
||||
tx, err := conn.Begin(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var applied bool
|
||||
err = tx.QueryRow(ctx, "SELECT EXISTS (SELECT 1 FROM schema_migrations WHERE version=$1)", name).Scan(&applied)
|
||||
if err == nil && !applied {
|
||||
var sql []byte
|
||||
sql, err = migrations.ReadFile(name)
|
||||
if err == nil {
|
||||
_, err = tx.Exec(ctx, string(sql), pgx.QueryExecModeSimpleProtocol)
|
||||
}
|
||||
if err == nil {
|
||||
_, err = tx.Exec(ctx, "INSERT INTO schema_migrations (version) VALUES ($1)", name)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
_ = tx.Rollback(ctx)
|
||||
return fmt.Errorf("migration %s: %w", name, err)
|
||||
}
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return fmt.Errorf("migration %s commit: %w", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user