82bb5708c8
artifactapi's schema was a ~180-line inline DDL blob re-executed on every start, growing an ALTER TABLE ... IF NOT EXISTS line per change with nothing recording what had run. golib/pg already owns that mechanic for the estate, so move the SQL into a versioned, embedded set and let the library apply it. - Depend on git.unkin.net/unkin/golib v0.1.0. - Move the DDL verbatim to migrations/0001_init.sql, embedded via the new migrations package, and build the pool with pg.NewMigrated (LockName "artifactapi-migrations"). The runner adds a cluster-wide advisory lock the old blob never took, so replicas starting together queue instead of racing each other through the DDL. - The live database has the schema but no schema_migrations, so its first start on this build re-runs 0001. Every statement is IF NOT EXISTS-guarded, so that run is a no-op landing only the tracking row; a container-backed test drops the row from a migrated database and asserts exactly that, and a static guard keeps future migrations additive and idempotent. - Keep config.DatabaseDSN as the DSN source rather than pg.DSNFromEnv: the variable names match, but golib has no default user or database name, and artifactapi documents and ships DBUSER/DBNAME defaults of "artifacts". The deployed env var contract is unchanged. - Guard the embedded set against migrations/ and pin the derived advisory key, so neither can drift unnoticed. - Plumb GOPRIVATE=git.unkin.net for the first cross-repo Go dependency: exported by the Makefile, set in the Dockerfile and the woodpecker Go steps, documented in the README.
36 lines
758 B
Go
36 lines
758 B
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"git.unkin.net/unkin/golib/pg"
|
|
|
|
"git.unkin.net/unkin/artifactapi/migrations"
|
|
)
|
|
|
|
// migrationLockName names the cluster-wide advisory lock the migration run
|
|
// contends for; golib derives the key as FNV-1a/64 of it. Replicas starting
|
|
// together queue on it instead of racing each other through the DDL.
|
|
const migrationLockName = "artifactapi-migrations"
|
|
|
|
type DB struct {
|
|
Pool *pgxpool.Pool
|
|
}
|
|
|
|
func New(dsn string) (*DB, error) {
|
|
ctx := context.Background()
|
|
pool, err := pg.NewMigrated(ctx, dsn, migrations.FS, pg.MigrateOptions{
|
|
LockName: migrationLockName,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DB{Pool: pool}, nil
|
|
}
|
|
|
|
func (db *DB) Close() {
|
|
db.Pool.Close()
|
|
}
|