// Package db owns the Controller's durable state in Postgres. It exposes // a small typed surface (UpsertAgent, InsertInstance, ListInstances, …) // built on pgx/v5 with a connection pool. Migrations are embedded so the // binary is self-contained. package db import ( "context" "fmt" "time" "github.com/jackc/pgx/v5/pgxpool" ) // DB wraps a pgx pool with our typed query methods. type DB struct { pool *pgxpool.Pool } // Open connects to Postgres using dsn, pings once to fail fast on bad // credentials or unreachable server, and runs any pending migrations. func Open(ctx context.Context, dsn string) (*DB, error) { cfg, err := pgxpool.ParseConfig(dsn) if err != nil { return nil, fmt.Errorf("parse db dsn: %w", err) } cfg.MaxConns = 10 cfg.MaxConnLifetime = 30 * time.Minute pool, err := pgxpool.NewWithConfig(ctx, cfg) if err != nil { return nil, fmt.Errorf("new pool: %w", err) } pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() if err := pool.Ping(pingCtx); err != nil { pool.Close() return nil, fmt.Errorf("ping db: %w", err) } if err := Migrate(ctx, pool); err != nil { pool.Close() return nil, fmt.Errorf("migrate: %w", err) } return &DB{pool: pool}, nil } // Close releases pool resources. func (db *DB) Close() { if db != nil && db.pool != nil { db.pool.Close() } } // Pool exposes the underlying pool for tests / advanced callers. func (db *DB) Pool() *pgxpool.Pool { return db.pool }