Add the golib scaffold and the pg module
Stand up the shared library with its first module: the Postgres plumbing every service currently copy-pastes — the DSN builder, the pool constructor, and the migration runner arrproxy proved out. - Add pg.DSNFromEnv, generalising the identical Sprintf builders in encapi, artifactapi and forgebot into one prefixed lookup with DATABASE_URL passthrough and libpq fallbacks. - Add pg.New and pg.NewMigrated, which ping before returning so an unreachable server fails at startup rather than on the first query. - Add pg.Migrate, lifting arrproxy's runner verbatim in semantics and generalising the hardcoded advisory-lock key to FNV-1a/64 of a caller-supplied name and the embedded set to an fs.FS. - Add pg/pgtest, unifying the encapi and artifactapi testcontainers helpers, with SkipIfShort so container-backed tests self-skip on the Docker-less Kubernetes runners. - Add the Makefile, README and pre-commit config, plus test, pre-commit and build pipelines on golib-ci.
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
package pg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// New opens a pgxpool against dsn and verifies it can reach the server before
|
||||
// returning. pgxpool connects lazily, so without the ping a bad address only
|
||||
// surfaces on the first query, long after startup has reported success.
|
||||
//
|
||||
// The caller owns the pool and must Close it. log may be nil.
|
||||
func New(ctx context.Context, dsn string, log *slog.Logger) (*pgxpool.Pool, error) {
|
||||
pool, err := pgxpool.New(ctx, dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect postgres: %w", err)
|
||||
}
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
pool.Close()
|
||||
return nil, fmt.Errorf("ping postgres: %w", err)
|
||||
}
|
||||
logger(log).Debug("postgres pool ready", "host", pool.Config().ConnConfig.Host)
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
// NewMigrated opens a pool and brings the schema up to date before returning
|
||||
// it, so a service cannot start serving against a half-migrated database. A
|
||||
// migration failure closes the pool and returns the error; callers treat it as
|
||||
// fatal.
|
||||
func NewMigrated(ctx context.Context, dsn string, fsys fs.FS, opts MigrateOptions) (*pgxpool.Pool, error) {
|
||||
pool, err := New(ctx, dsn, opts.Logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := Migrate(ctx, pool, fsys, opts); err != nil {
|
||||
pool.Close()
|
||||
return nil, err
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
Reference in New Issue
Block a user