Documentation
¶
Overview ¶
Package postgres provides a pgxpool.Pool constructor with the operational defaults the source applications lacked: slow-query tracing (see slow_query_tracer.go), Prometheus pool metrics (see metrics.go), a hard startup failure instead of a silent no-op when TLS is required but the DSN does not enable it, and server-side statement/lock/idle-in-transaction timeouts applied on every new connection.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Config ¶
type Config struct {
// URL is the PostgreSQL connection string (DSN), e.g.
// "postgres://user:pass@host:5432/db?sslmode=require".
URL string
MaxConns int32
MinConns int32
MaxConnLifetime time.Duration
MaxConnIdleTime time.Duration
ConnectTimeout time.Duration
// RequireTLS enforces a minimum TLS 1.2 connection. If true and the parsed
// DSN does not enable TLS (e.g. sslmode=disable), NewPool fails fast with
// an error instead of silently opening a plaintext connection. See
// applyTLSConfig for the historical bug this fixes.
RequireTLS bool
// StatementTimeout caps how long a single SQL statement may run before
// PostgreSQL cancels it (server-side `SET statement_timeout`). Protects
// against a runaway query holding a connection (and any locks it took)
// indefinitely. Zero uses defaultStatementTimeout (30s); a negative value
// disables the timeout explicitly.
StatementTimeout time.Duration
// LockTimeout caps how long a statement will wait to acquire a row/table
// lock before failing (server-side `SET lock_timeout`). Protects against a
// request queuing behind a lock held by another slow transaction instead
// of failing fast and letting the caller retry or surface an error. Zero
// uses defaultLockTimeout (5s); a negative value disables it explicitly.
LockTimeout time.Duration
// IdleInTransactionSessionTimeout caps how long a connection may sit idle
// while inside an open transaction (server-side
// `SET idle_in_transaction_session_timeout`) before PostgreSQL kills the
// session.
//
// This is the timeout that matters most of the three: it is the only
// server-side backstop against a handler bug that begins a transaction,
// then hangs (a stuck downstream call, a forgotten context deadline, a
// panic recovered above the DB layer without rolling back) while still
// holding the transaction's row locks. Without it, that one stuck request
// can block every other request that needs the same rows — potentially
// the whole pool, if MaxConns is small — until the process is restarted.
// statement_timeout does not help here because no statement is running;
// the connection is simply idle with an open transaction.
//
// Zero uses defaultIdleInTransactionSessionTimeout (60s); a negative value
// disables it explicitly.
IdleInTransactionSessionTimeout time.Duration
// AfterConnect, if set, runs after this package's own per-connection setup
// (TLS, timeouts, tracer) on every new physical connection. Use it for
// consumer-specific per-connection work — for example, registering a
// custom pgtype codec such as jackc/pgx-shopspring-decimal, the way
// go-crucible's original constructor did inline. Returning an error here
// fails that connection attempt.
AfterConnect func(ctx context.Context, conn *pgx.Conn) error
}
Config configures NewPool. Adapters own their Config struct rather than reading a global application config, so this type carries only what a pgxpool.Pool needs — the consuming application decides how (env vars, a flags package, hardcoded test values, ...) to fill it in.
type PoolMetricsCollector ¶
type PoolMetricsCollector struct {
// contains filtered or unexported fields
}
PoolMetricsCollector exports pgxpool stats as Prometheus metrics.
Unlike a package-level prometheus.MustRegister, NewPoolMetricsCollector is safe to call more than once against the same Registerer: a prometheus.AlreadyRegisteredError is handled by reusing the already registered collector instead of panicking. This matters for a library — a consumer may build two pools (e.g. in a test that constructs a fresh app per test case) and must not have the second one panic the process.
Reusing the existing collector means a second NewPoolMetricsCollector call against the same Registerer returns the FIRST pool's collector, still bound to the first pool. If a consumer genuinely needs metrics for two distinct, simultaneously-live pools, register each with its own prometheus.Registerer (e.g. a dedicated prometheus.NewRegistry()) rather than sharing one.
func NewPoolMetricsCollector ¶
func NewPoolMetricsCollector(pool *pgxpool.Pool, reg prometheus.Registerer) (*PoolMetricsCollector, error)
NewPoolMetricsCollector creates a collector for pool and registers it on reg. If reg is nil, prometheus.DefaultRegisterer is used.
func (*PoolMetricsCollector) Collect ¶
func (c *PoolMetricsCollector) Collect(ch chan<- prometheus.Metric)
func (*PoolMetricsCollector) Describe ¶
func (c *PoolMetricsCollector) Describe(ch chan<- *prometheus.Desc)
type SlowQueryTracer ¶
type SlowQueryTracer struct {
// contains filtered or unexported fields
}
SlowQueryTracer implements pgx.QueryTracer and logs queries that exceed the configured threshold.
func NewSlowQueryTracer ¶
func NewSlowQueryTracer(logger *slog.Logger, threshold time.Duration) *SlowQueryTracer
NewSlowQueryTracer creates a tracer that logs queries slower than threshold.
func (*SlowQueryTracer) TraceQueryEnd ¶
func (t *SlowQueryTracer) TraceQueryEnd(ctx context.Context, _ *pgx.Conn, data pgx.TraceQueryEndData)
func (*SlowQueryTracer) TraceQueryStart ¶
func (t *SlowQueryTracer) TraceQueryStart(ctx context.Context, _ *pgx.Conn, data pgx.TraceQueryStartData) context.Context