feat: run all migrations in a transaction (#10966)

Updates coder/customers#365

This PR updates our migration framework to run all migrations in a single transaction. This is the same behavior we had in v1 and ensures that failed migrations don't bring the whole deployment down. If a migration fails now, it will automatically be rolled back to the previous version, allowing the deployment to continue functioning.
This commit is contained in:
Colin Adler
2023-12-01 16:11:10 -06:00
committed by GitHub
parent 60d0aa6930
commit 8e684c8195
109 changed files with 243 additions and 372 deletions

View File

@ -9,7 +9,6 @@ import (
"os"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/postgres"
"github.com/golang-migrate/migrate/v4/source"
"github.com/golang-migrate/migrate/v4/source/iofs"
"golang.org/x/xerrors"
@ -27,22 +26,16 @@ func setup(db *sql.DB) (source.Driver, *migrate.Migrate, error) {
// migration_cursor is a v1 migration table. If this exists, we're on v1.
// Do no run v2 migrations on a v1 database!
row := db.QueryRowContext(ctx, "SELECT * FROM migration_cursor;")
if row.Err() == nil {
return nil, nil, xerrors.Errorf("currently connected to a Coder v1 database, aborting database setup")
row := db.QueryRowContext(ctx, "SELECT 1 FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = 'migration_cursor';")
var v1Exists int
if row.Scan(&v1Exists) == nil {
return nil, nil, xerrors.New("currently connected to a Coder v1 database, aborting database setup")
}
// there is a postgres.WithInstance() method that takes the DB instance,
// but, when you close the resulting Migrate, it closes the DB, which
// we don't want. Instead, create just a connection that will get closed
// when migration is done.
conn, err := db.Conn(ctx)
dbDriver := &pgTxnDriver{ctx: context.Background(), db: db}
err = dbDriver.ensureVersionTable()
if err != nil {
return nil, nil, xerrors.Errorf("postgres connection: %w", err)
}
dbDriver, err := postgres.WithConnection(ctx, conn, &postgres.Config{})
if err != nil {
return nil, nil, xerrors.Errorf("wrap postgres connection: %w", err)
return nil, nil, xerrors.Errorf("ensure version table: %w", err)
}
m, err := migrate.NewWithInstance("", sourceDriver, "", dbDriver)