dbpool

package
v0.27.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PoolLabel

func PoolLabel(dataSource string) string

PoolLabel derives the "pool" label value from a PostgreSQL data source: the host, port and database name, with no credential of any kind - unlike a redacted DSN, which would still carry a password if the connection string put it in a field redactDataSource (postgres/notifier.go) does not expect. The identity that matters for the pool label is "which server and database", not "how was it addressed", so the normalized "host:port/db" form is used for both of PostgreSQL's two accepted DSN syntaxes, and a connection string that parses as neither falls back to a short, stable hash so a label is never raw and unredacted even in that path.

Types

type Metrics

type Metrics struct {
	// InUse is the number of connections currently checked out of the pool,
	// sampled instantaneously (not a max-since-last-sample - see Sampler).
	// Compare against MaxOpen for saturation; a value pinned at MaxOpen
	// together with a rising WaitCount is the pool-exhaustion signature that
	// made #2474 need a goroutine dump to diagnose.
	InUse metrics.Gauge
	// MaxOpen is the pool's configured connection limit (sql.DBStats.MaxOpenConnections),
	// exported so dashboards can compute saturation (InUse/MaxOpen) rather than
	// an absolute connection count that means nothing without cross-referencing
	// the node's core.yaml.
	MaxOpen metrics.Gauge
	// WaitCountTotal is the cumulative number of connection checkouts that had
	// to wait because the pool was at its limit. Unlike InUse this is
	// loss-free between samples: database/sql increments it once per blocked
	// checkout regardless of how often it is scraped, so a burst too short
	// for the gauge to catch still shows up here.
	WaitCountTotal metrics.Counter
	// WaitSecondsTotal is the cumulative time callers spent waiting for a
	// connection. Divided by WaitCountTotal (as a rate ratio, not a raw
	// division, since both are counters) it gives the mean wait per checkout:
	// rate(wait_seconds_total[$__rate_interval]) / rate(wait_count_total[$__rate_interval]).
	WaitSecondsTotal metrics.Counter
}

Metrics holds the instrumentation of a Sampler. Unlike most Panurus metrics families, these are exported under an explicitly set Namespace/Subsystem rather than letting the calling package derive the name - see "How a metric name is built" and "Metric hygiene" in docs/development/metrics.md. This is the first Panurus metric family to do so, a deliberate exception rather than an oversight: it is the fix the Metric hygiene section recommends for new metrics, and there is no existing dashboard or alert to break.

func NewMetrics

func NewMetrics(p metrics.Provider) *Metrics

NewMetrics creates the connection-pool instrumentation. A nil provider discards everything, so a Sampler built with no metrics configured needs no special case.

type Sampler

type Sampler struct {
	// contains filtered or unexported fields
}

Sampler periodically reads (*sql.DB).Stats() for every registered pool and reports it through Metrics.

It exists because Panurus never checks out a connection itself - the pool lives inside fabric-smart-client's postgres.Open and reaches Panurus only as a *sql.DB - so (*sql.DB).Stats() is the only available signal, and metrics.Provider has no way to register a custom collector that would be evaluated at scrape time. Something has to poll.

The InUse and MaxOpen gauges are deliberately instantaneous, not a max-since-last-sample: sampling at DefaultPoolMetricsInterval (1s) makes a sustained exhaustion episode - the kind that actually hangs Select calls, as in #2474 - visible on most samples regardless, and a transient burst too short to land on a sample still shows up in WaitCountTotal, which database/sql increments once per blocked checkout independent of how often anything scrapes it. Peak occupancy is a PromQL concern (max_over_time(db_pool_in_use[...])), not something the sampler itself needs to track. Do not raise the interval back toward something like 10s without re-reading this: at a slower poll the gauge can sit well below MaxOpen throughout an episode that was in fact permanently exhausted between samples.

func NewSampler

func NewSampler(cfg driver3.Config, p metrics.Provider) *Sampler

NewSampler builds a Sampler from cfg's token.storage.db.poolMetricsInterval (DefaultPoolMetricsInterval when unset) and p. A nil p (or a cfg that fails to parse) does not prevent construction - see Start, which no-ops when the interval is 0, and NewMetrics's disabled-provider fallback.

func (*Sampler) Register

func (s *Sampler) Register(key, label string, db *sql.DB)

Register adds db to the set of pools this sampler reports on, keyed by key (the pool's identity - callers pass the raw data source, since every store sharing a data source shares one *sql.DB and must be registered under one key) with label as the exported "pool" label value. Registering the same key again is a no-op: N stores on one data source must produce one series, not N.

func (*Sampler) Sample

func (s *Sampler) Sample()

Sample reads Stats() for every registered pool once and updates Metrics. Exported so tests can drive a deterministic pass without waiting on the ticker; Start calls it on the same interval it reports.

func (*Sampler) Start

func (s *Sampler) Start() error

Start begins the sampling loop. A zero interval disables the sampler: Start returns nil without starting a goroutine, so an operator who does not scrape Prometheus pays nothing for it. Calling Start twice is a no-op.

func (*Sampler) Stop

func (s *Sampler) Stop() error

Stop ends the sampling loop and waits for its goroutine to exit, so no sampler outlives an in-process SDK restart - the same reason SDK.Stop already stops the ledger drift checks sweeps (token/sdk/dig/sdk.go). Calling Stop before Start, or twice, is a no-op.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL