diff --git a/go/vt/dbconnpool/connection_pool.go b/go/vt/dbconnpool/connection_pool.go index 9865efdada3..4814ae3aedf 100644 --- a/go/vt/dbconnpool/connection_pool.go +++ b/go/vt/dbconnpool/connection_pool.go @@ -36,8 +36,16 @@ import ( // PooledDBConnection objects. type ConnectionPool struct { *smartconnpool.ConnPool[*DBConnection] + + name string } +// usedNames is for preventing expvar from panicking. Tests +// create pool objects multiple time. If a name was previously +// used, expvar initialization is skipped. +// through non-test code. +var usedNames = make(map[string]bool) + // NewConnectionPool creates a new ConnectionPool. The name is used // to publish stats only. func NewConnectionPool(name string, stats *servenv.Exporter, capacity int, idleTimeout time.Duration, maxLifetime time.Duration, dnsResolutionFrequency time.Duration) *ConnectionPool { @@ -47,7 +55,18 @@ func NewConnectionPool(name string, stats *servenv.Exporter, capacity int, idleT MaxLifetime: maxLifetime, RefreshInterval: dnsResolutionFrequency, } - return &ConnectionPool{ConnPool: smartconnpool.NewPool(&config)} + cp := &ConnectionPool{ConnPool: smartconnpool.NewPool(&config), name: name} + if name == "" || usedNames[name] { + return cp + } + usedNames[name] = true + + if stats == nil { + // This is unnamed exported so it will use the stats functions directly when adding to the expvar. + stats = servenv.NewExporter("", "") + } + cp.ConnPool.RegisterStats(stats, name) + return cp } // Open must be called before starting to use the pool. diff --git a/go/vt/vttablet/endtoend/config_test.go b/go/vt/vttablet/endtoend/config_test.go index 3902113f354..134ff4be162 100644 --- a/go/vt/vttablet/endtoend/config_test.go +++ b/go/vt/vttablet/endtoend/config_test.go @@ -279,6 +279,29 @@ func TestQueryTimeout(t *testing.T) { compareIntDiff(t, vend, "Kills/Connections", vstart, 1) } +// TestHeartbeatMetric validates the heartbeat metrics exists from the connection pool. +func TestHeartbeatMetric(t *testing.T) { + tcases := []struct { + metricName string + exp any + }{{ + metricName: "HeartbeatWriteAppPoolCapacity", + exp: 2, + }, { + metricName: "HeartbeatWriteAllPrivsPoolCapacity", + exp: 2, + }} + + metrics := framework.DebugVars() + for _, tcase := range tcases { + t.Run(tcase.metricName, func(t *testing.T) { + mValue, exists := metrics[tcase.metricName] + require.True(t, exists, "metric %s not found", tcase.metricName) + require.EqualValues(t, tcase.exp, mValue, "metric %s value is %d, want %d", tcase.metricName, mValue, tcase.exp) + }) + } +} + func changeVar(t *testing.T, name, value string) (revert func()) { t.Helper()