Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-09-16 15:52:05 -04:00
pkg-proxy/internal/database/postgres_pool_test.go

57 lines
1.8 KiB
Go
Raw Permalink Normal View History

fix(database): set connection pool limits for Postgres (#350) * fix(database): set connection pool limits for Postgres OpenPostgres returned sqlx.Open's handle with database/sql's defaults: no cap on open connections and two idle ones. Under load nearly every request opened a new Postgres session, ran its few statements and closed it again, paying a backend fork and SCRAM authentication each time. Set the pool limits the issue suggests: 32 open and 32 idle connections, idle connections closed after 5 minutes and every connection recycled after 30 minutes. The values live in named constants because the mnd linter rejects the literals inline. The test (skipped without PROXY_DATABASE_URL, like the other Postgres tests) takes 16 connections from the pool, releases them and checks that all 16 stay idle; with the default pool only two survive. Fixes #323 * test(database): pin the pool properties instead of the wiring Review pointed out that the pool test compared MaxOpenConnections to the constant it was set from, so the assertion followed the constant and would have accepted postgresMaxOpenConns = 0 (unlimited), and that its burst of 16 only proved MaxIdleConns >= 16. The burst now takes postgresMaxIdleConns connections, so every configured idle slot has to survive the release, and the open cap is checked to be finite and large enough for that burst before any connection is taken, so a cap below the idle count fails fast instead of blocking in db.Conn. The doc comment now says which settings the test covers; the idle-time and lifetime settings only show up in DBStats.MaxIdleTimeClosed and MaxLifetimeClosed after minutes of wall-clock time and stay unexercised. * test(database): guard the pool test against a vacuous burst Review showed that with the burst tied to postgresMaxIdleConns the test also passed for a constant of 2, database/sql's default, or of 0, where it took no connections at all. It now fails outright unless the configured idle count exceeds the default, and every connection it takes is released in a cleanup, so an assertion failure mid-burst no longer leaves sessions open for the rest of the test binary.
2026-09-16 09:13:38 +02:00
package database
import (
"context"
"database/sql"
"os"
"testing"
)
// TestOpenPostgresKeepsConnectionsIdle checks the connection-count limits
// OpenPostgres sets: the open cap admits a burst of postgresMaxIdleConns
// connections, and releasing them again leaves all of them idle in the pool.
// database/sql's default keeps only two, so the next burst would open a new
// Postgres session for almost every request. The idle-time and lifetime
// settings are not exercised here.
func TestOpenPostgresKeepsConnectionsIdle(t *testing.T) {
url := os.Getenv("PROXY_DATABASE_URL")
if url == "" {
t.Skip("PROXY_DATABASE_URL not set, skipping postgres pool test")
}
db, err := OpenPostgres(url)
if err != nil {
t.Fatalf("OpenPostgres failed: %v", err)
}
defer func() { _ = db.Close() }()
const burst = postgresMaxIdleConns
// database/sql keeps two idle connections by default; a burst that small
// could not tell the tuned pool from the default one.
if burst <= 2 {
t.Fatalf("postgresMaxIdleConns = %d, want more than database/sql's default of 2", burst)
}
if got := db.Stats().MaxOpenConnections; got <= 0 || got < burst {
t.Fatalf("MaxOpenConnections = %d, want a cap of at least %d", got, burst)
}
conns := make([]*sql.Conn, 0, burst)
for range burst {
conn, err := db.Conn(context.Background())
if err != nil {
t.Fatalf("taking connection %d: %v", len(conns)+1, err)
}
t.Cleanup(func() { _ = conn.Close() }) // release the session if an assertion below fails
conns = append(conns, conn)
}
if got := db.Stats().InUse; got != burst {
t.Fatalf("InUse = %d while holding %d connections", got, burst)
}
for _, conn := range conns {
_ = conn.Close()
}
if got := db.Stats().Idle; got != burst {
t.Errorf("Idle = %d after releasing %d connections, want all of them kept", got, burst)
}
}