// Package db provides PostgreSQL connection management using pgx. package db import ( "context" "fmt" "log/slog" "time" "github.com/jackc/pgx/v5/pgxpool" ) // Pool wraps a pgx connection pool with lifecycle helpers. type Pool struct { *pgxpool.Pool } // New creates a connection pool from a PostgreSQL DSN. func New(dsn string) (*Pool, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cfg, err := pgxpool.ParseConfig(dsn) if err != nil { return nil, fmt.Errorf("parse pgx config: %w", err) } // Sensible defaults cfg.MaxConns = 20 cfg.MinConns = 2 cfg.MaxConnLifetime = 30 * time.Minute cfg.MaxConnIdleTime = 10 * time.Minute cfg.HealthCheckPeriod = 5 * time.Second pool, err := pgxpool.NewWithConfig(ctx, cfg) if err != nil { return nil, fmt.Errorf("create pgx pool: %w", err) } if err := pool.Ping(ctx); err != nil { pool.Close() return nil, fmt.Errorf("ping database: %w", err) } slog.Info("database connected", "pool", cfg.ConnConfig.Database) return &Pool{Pool: pool}, nil } // Close shuts down the pool gracefully. func (p *Pool) Close() { if p.Pool != nil { p.Pool.Close() slog.Info("database pool closed") } } // Health returns nil if the database is reachable. func (p *Pool) Health(ctx context.Context) error { ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() return p.Ping(ctx) }