diff --git a/cmd/api/main.go b/cmd/api/main.go index 4b3ada6..71b71cd 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -67,7 +67,7 @@ func run() error { defer cancelConnect() // Adaptador de almacenamiento: PostgreSQL + TimescaleDB. La conexión es perezosa (la API arranca aunque la // base no responda todavía) y /health informa el estado real. - pool, err := postgres.Connect(connectCtx, cfg.Database.URL, int32(cfg.Database.MaxConns)) + pool, err := postgres.Connect(connectCtx, cfg.Database.URL, cfg.Database.MaxConns) if err != nil { return err } diff --git a/internal/storage/postgres/postgres.go b/internal/storage/postgres/postgres.go index d3146a8..9dcaff1 100644 --- a/internal/storage/postgres/postgres.go +++ b/internal/storage/postgres/postgres.go @@ -20,14 +20,21 @@ type Pool struct { Pool *pgxpool.Pool } +// MaxPoolConns acota DATABASE_MAX_CONNS: más conexiones por instancia de la API no tiene sentido y el pool +// de pgx usa int32. +const MaxPoolConns = 1000 + // Connect crea el pool a partir de la URL de PostgreSQL. No abre conexiones hasta la primera consulta. -func Connect(ctx context.Context, url string, maxConns int32) (*Pool, error) { +func Connect(ctx context.Context, url string, maxConns int) (*Pool, error) { cfg, err := pgxpool.ParseConfig(url) if err != nil { return nil, fmt.Errorf("DATABASE_URL inválida: %w", err) } + if maxConns > MaxPoolConns { + return nil, fmt.Errorf("DATABASE_MAX_CONNS no puede superar %d", MaxPoolConns) + } if maxConns > 0 { - cfg.MaxConns = maxConns + cfg.MaxConns = int32(maxConns) } cfg.MaxConnIdleTime = 5 * time.Minute pool, err := pgxpool.NewWithConfig(ctx, cfg) diff --git a/internal/storage/postgres/postgres_test.go b/internal/storage/postgres/postgres_test.go new file mode 100644 index 0000000..c2024aa --- /dev/null +++ b/internal/storage/postgres/postgres_test.go @@ -0,0 +1,28 @@ +package postgres + +import ( + "context" + "strings" + "testing" +) + +// La URL es válida pero no se conecta: Connect no abre conexiones hasta la primera consulta. +const lazyURL = "postgres://smartpot:clave@127.0.0.1:1/smartpot?sslmode=disable" + +func TestConnectRechazaPoolDesmedido(t *testing.T) { + _, err := Connect(context.Background(), lazyURL, MaxPoolConns+1) + if err == nil || !strings.Contains(err.Error(), "DATABASE_MAX_CONNS") { + t.Fatalf("se esperaba el error de DATABASE_MAX_CONNS, llegó %v", err) + } +} + +func TestConnectAplicaElMaximoDeConexiones(t *testing.T) { + pool, err := Connect(context.Background(), lazyURL, 7) + if err != nil { + t.Fatalf("Connect: %v", err) + } + defer pool.Pool.Close() + if got := pool.Pool.Config().MaxConns; got != 7 { + t.Fatalf("MaxConns = %d, se esperaba 7", got) + } +}