Files
im_server/server/metrics/db_pool.go
T

81 lines
3.4 KiB
Go

package metrics
import (
"database/sql"
"strings"
"sync"
"github.com/prometheus/client_golang/prometheus"
)
type dbStatsProvider interface {
Stats() sql.DBStats
}
type dbPoolCollector struct {
pools sync.Map
desc map[string]*prometheus.Desc
}
func newDBPoolCollector() *dbPoolCollector {
labels := []string{"pod", "pool"}
return &dbPoolCollector{desc: map[string]*prometheus.Desc{
"max_open": prometheus.NewDesc("im_db_pool_max_open_connections", "Configured maximum open database connections.", labels, nil),
"open": prometheus.NewDesc("im_db_pool_open_connections", "Current open database connections.", labels, nil),
"in_use": prometheus.NewDesc("im_db_pool_in_use_connections", "Current database connections in use.", labels, nil),
"idle": prometheus.NewDesc("im_db_pool_idle_connections", "Current idle database connections.", labels, nil),
"wait": prometheus.NewDesc("im_db_pool_wait_total", "Total waits for a database connection.", labels, nil),
"wait_duration": prometheus.NewDesc("im_db_pool_wait_duration_seconds_total", "Total time spent waiting for database connections.", labels, nil),
"max_idle_closed": prometheus.NewDesc("im_db_pool_max_idle_closed_total", "Connections closed because the idle limit was exceeded.", labels, nil),
"max_lifetime_closed": prometheus.NewDesc("im_db_pool_max_lifetime_closed_total", "Connections closed because their lifetime expired.", labels, nil),
}}
}
func (c *dbPoolCollector) Describe(ch chan<- *prometheus.Desc) {
for _, desc := range c.desc {
ch <- desc
}
}
func (c *dbPoolCollector) Collect(ch chan<- prometheus.Metric) {
pod := metricPodName()
c.pools.Range(func(key, value any) bool {
pool, ok := key.(string)
provider, providerOK := value.(dbStatsProvider)
if !ok || !providerOK {
return true
}
stats := provider.Stats()
labels := []string{pod, pool}
ch <- prometheus.MustNewConstMetric(c.desc["max_open"], prometheus.GaugeValue, float64(stats.MaxOpenConnections), labels...)
ch <- prometheus.MustNewConstMetric(c.desc["open"], prometheus.GaugeValue, float64(stats.OpenConnections), labels...)
ch <- prometheus.MustNewConstMetric(c.desc["in_use"], prometheus.GaugeValue, float64(stats.InUse), labels...)
ch <- prometheus.MustNewConstMetric(c.desc["idle"], prometheus.GaugeValue, float64(stats.Idle), labels...)
ch <- prometheus.MustNewConstMetric(c.desc["wait"], prometheus.CounterValue, float64(stats.WaitCount), labels...)
ch <- prometheus.MustNewConstMetric(c.desc["wait_duration"], prometheus.CounterValue, stats.WaitDuration.Seconds(), labels...)
ch <- prometheus.MustNewConstMetric(c.desc["max_idle_closed"], prometheus.CounterValue, float64(stats.MaxIdleClosed), labels...)
ch <- prometheus.MustNewConstMetric(c.desc["max_lifetime_closed"], prometheus.CounterValue, float64(stats.MaxLifetimeClosed), labels...)
return true
})
}
var dbPools = newDBPoolCollector()
// RegisterDBPool exposes database/sql pool pressure for one logical database.
// Registering the same name again replaces the provider, which keeps tests and
// adapter reopen operations safe.
func RegisterDBPool(name string, provider dbStatsProvider) {
if provider == nil {
return
}
name = strings.TrimSpace(name)
if name == "" {
name = "default"
}
dbPools.pools.Store(name, provider)
}
func init() {
Registry.MustRegister(dbPools)
}