From 39ddec7204b26a25263fe7b4d03a98746774a0c0 Mon Sep 17 00:00:00 2001 From: djshow832 Date: Fri, 11 Sep 2026 21:51:47 +0800 Subject: [PATCH] This is an automated cherry-pick of #1212 Resolve conflicts for release-1.3: drop the meter field, the manual health override, the ConfigManager interface, and the client-mode replayer cmd that are absent on this branch. Add UnhealthyReason to HealthInfo so DebugHealth can report the reason. Signed-off-by: ti-chi-bot Co-authored-by: Cursor --- lib/config/health.go | 3 +- pkg/manager/health/health.go | 65 +++++++++++++++++++++++++ pkg/manager/health/health_test.go | 79 +++++++++++++++++++++++++++++++ pkg/proxy/proxy.go | 75 ++++++++++++++--------------- pkg/proxy/proxy_test.go | 41 +++++++--------- pkg/server/api/debug.go | 10 ++-- pkg/server/api/debug_test.go | 7 ++- pkg/server/api/server.go | 27 ++++++----- pkg/server/api/server_test.go | 3 ++ pkg/server/server.go | 25 ++++++++-- 10 files changed, 250 insertions(+), 85 deletions(-) create mode 100644 pkg/manager/health/health.go create mode 100644 pkg/manager/health/health_test.go diff --git a/lib/config/health.go b/lib/config/health.go index 5c13bc0e3..6dfcd024f 100644 --- a/lib/config/health.go +++ b/lib/config/health.go @@ -6,7 +6,8 @@ package config import "time" type HealthInfo struct { - ConfigChecksum uint32 `json:"config_checksum"` + ConfigChecksum uint32 `json:"config_checksum"` + UnhealthyReason string `json:"unhealthy_reason,omitempty"` } const ( diff --git a/pkg/manager/health/health.go b/pkg/manager/health/health.go new file mode 100644 index 000000000..5b5a1200b --- /dev/null +++ b/pkg/manager/health/health.go @@ -0,0 +1,65 @@ +// Copyright 2026 PingCAP, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package health + +import "sync/atomic" + +// Manager aggregates the signals that decide whether the TiProxy instance is +// serving and whether it should keep accepting new connections. Each public +// method below evaluates its own condition independently, so reordering one +// signal never silently changes another consumer's behavior. +type Manager struct { + // shuttingDown means the instance is in graceful shutdown. It only affects + // Healthy (so DebugHealth reports unhealthy and the LB drains); it does NOT + // reject new connections, since the proxy keeps serving until its listeners + // are closed. + shuttingDown atomic.Bool + ready func() bool + rejectCheck func() (bool, string) +} + +// NewManager creates a Manager. +// - ready reports whether the namespace manager is ready (init phase when false). +// - rejectCheck reports whether new connections should be rejected because of +// memory pressure, together with a human-readable reason. +func NewManager(ready func() bool, rejectCheck func() (bool, string)) *Manager { + return &Manager{ready: ready, rejectCheck: rejectCheck} +} + +// PreClose marks the instance as gracefully shutting down. Idempotent; safe to +// call from PreClose paths. +func (m *Manager) PreClose() { + m.shuttingDown.Store(true) +} + +// Healthy reports whether the instance is fully serving. Used by DebugHealth. +// It returns false (with a reason) during init, rejectConns, and graceful +// shutdown. +func (m *Manager) Healthy() (bool, string) { + if m.shuttingDown.Load() { + return false, "server is shutting down" + } + if m.rejectCheck != nil { + if reject, reason := m.rejectCheck(); reject { + return false, reason + } + } + if m.ready != nil && !m.ready() { + return false, "server is not ready" + } + return true, "" +} + +// RejectConns reports whether new connections should be rejected and returns a +// reason string. Used by the proxy server. It returns true only on memory +// pressure; graceful shutdown does NOT reject here, because the proxy keeps +// accepting until its listeners are closed. +func (m *Manager) RejectConns() (bool, string) { + if m.rejectCheck != nil { + if reject, reason := m.rejectCheck(); reject { + return true, reason + } + } + return false, "" +} diff --git a/pkg/manager/health/health_test.go b/pkg/manager/health/health_test.go new file mode 100644 index 000000000..dd487bfeb --- /dev/null +++ b/pkg/manager/health/health_test.go @@ -0,0 +1,79 @@ +// Copyright 2026 PingCAP, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package health + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestManager(t *testing.T) { + ready := true + reject := false + mgr := NewManager( + func() bool { return ready }, + func() (bool, string) { + if !reject { + return false, "" + } + return true, "high memory usage" + }, + ) + + // Fully serving. + ok, reason := mgr.Healthy() + require.True(t, ok) + require.Empty(t, reason) + rejectConn, reason := mgr.RejectConns() + require.False(t, rejectConn) + require.Empty(t, reason) + + // Init phase: Healthy reports not ready, but the proxy still accepts. + ready = false + ok, reason = mgr.Healthy() + require.False(t, ok) + require.Equal(t, "server is not ready", reason) + rejectConn, reason = mgr.RejectConns() + require.False(t, rejectConn) + require.Empty(t, reason) + + // Memory pressure: both consumers see the reject reason. + ready = true + reject = true + ok, reason = mgr.Healthy() + require.False(t, ok) + require.Equal(t, "high memory usage", reason) + rejectConn, reason = mgr.RejectConns() + require.True(t, rejectConn) + require.Equal(t, "high memory usage", reason) + + // Graceful shutdown alone (no memory pressure): Healthy reports unhealthy, + // but the proxy keeps accepting until its listeners are closed. + reject = false + mgr.PreClose() + ok, reason = mgr.Healthy() + require.False(t, ok) + require.Equal(t, "server is shutting down", reason) + rejectConn, reason = mgr.RejectConns() + require.False(t, rejectConn) + require.Empty(t, reason) +} + +func TestManagerNilChecks(t *testing.T) { + // Nil ready/rejectCheck must not panic and default to serving/accepting. + mgr := NewManager(nil, nil) + ok, reason := mgr.Healthy() + require.True(t, ok) + require.Empty(t, reason) + rejectConn, reason := mgr.RejectConns() + require.False(t, rejectConn) + require.Empty(t, reason) + + mgr.PreClose() + ok, _ = mgr.Healthy() + require.False(t, ok) + rejectConn, _ = mgr.RejectConns() + require.False(t, rejectConn) +} diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go index 681eecbd1..09a72e515 100644 --- a/pkg/proxy/proxy.go +++ b/pkg/proxy/proxy.go @@ -14,7 +14,6 @@ import ( "github.com/pingcap/tiproxy/lib/util/errors" "github.com/pingcap/tiproxy/pkg/manager/cert" "github.com/pingcap/tiproxy/pkg/manager/id" - mgrmem "github.com/pingcap/tiproxy/pkg/manager/memory" "github.com/pingcap/tiproxy/pkg/metrics" "github.com/pingcap/tiproxy/pkg/proxy/backend" "github.com/pingcap/tiproxy/pkg/proxy/client" @@ -40,22 +39,26 @@ type serverState struct { } type SQLServer struct { - listeners []net.Listener - addrs []string - logger *zap.Logger - certMgr *cert.CertManager - idMgr *id.IDManager - memUsage memoryStateProvider - hsHandler backend.HandshakeHandler - cpt capture.Capture - wg waitgroup.WaitGroup - cancelFunc context.CancelFunc + listeners []net.Listener + addrs []string + logger *zap.Logger + certMgr *cert.CertManager + idMgr *id.IDManager + connBufferUpdater connBufferMemoryUpdater + health connAcceptor + hsHandler backend.HandshakeHandler + cpt capture.Capture + wg waitgroup.WaitGroup + cancelFunc context.CancelFunc mu serverState } -type memoryStateProvider interface { - ShouldRejectNewConn() (bool, mgrmem.UsageSnapshot, float64) +// connAcceptor reports whether the proxy should reject new connections and +// provides a reason for the reject log. Satisfied by *health.Manager; defined +// here so the proxy package does not depend on the health package. +type connAcceptor interface { + RejectConns() (bool, string) } type connBufferMemoryUpdater interface { @@ -72,15 +75,16 @@ func estimateConnBufferMemDelta(bufferSize int) int64 { // NewSQLServer creates a new SQLServer. func NewSQLServer(logger *zap.Logger, cfg *config.Config, certMgr *cert.CertManager, idMgr *id.IDManager, cpt capture.Capture, - hsHandler backend.HandshakeHandler, memUsage memoryStateProvider) (*SQLServer, error) { + hsHandler backend.HandshakeHandler, connBufferUpdater connBufferMemoryUpdater, health connAcceptor) (*SQLServer, error) { var err error s := &SQLServer{ - logger: logger, - certMgr: certMgr, - idMgr: idMgr, - memUsage: memUsage, - hsHandler: hsHandler, - cpt: cpt, + logger: logger, + certMgr: certMgr, + idMgr: idMgr, + connBufferUpdater: connBufferUpdater, + health: health, + hsHandler: hsHandler, + cpt: cpt, mu: serverState{ clients: make(map[uint64]*client.ClientConnection), }, @@ -159,7 +163,7 @@ func (s *SQLServer) Run(ctx context.Context, cfgch <-chan *config.Config) { } func (s *SQLServer) onConn(ctx context.Context, conn net.Conn, addr string) { - if s.rejectConnByMemory(conn) { + if s.rejectConn(conn) { return } @@ -167,9 +171,7 @@ func (s *SQLServer) onConn(ctx context.Context, conn net.Conn, addr string) { connBufferUpdater connBufferMemoryUpdater connBufferMemDelta int64 ) - if s.memUsage != nil { - connBufferUpdater, _ = s.memUsage.(connBufferMemoryUpdater) - } + connBufferUpdater = s.connBufferUpdater tcpKeepAlive, logger, connID, clientConn := func() (bool, *zap.Logger, uint64, *client.ClientConnection) { s.mu.Lock() @@ -234,24 +236,19 @@ func (s *SQLServer) onConn(ctx context.Context, conn net.Conn, addr string) { clientConn.Run(ctx) } -func (s *SQLServer) rejectConnByMemory(conn net.Conn) bool { - if s.memUsage == nil { +func (s *SQLServer) rejectConn(conn net.Conn) bool { + if s.health == nil { return false } - reject, snapshot, threshold := s.memUsage.ShouldRejectNewConn() - if !reject { - return false + if reject, reason := s.health.RejectConns(); reject { + metrics.RejectConnCounter.WithLabelValues("memory").Inc() + s.logger.Warn("reject connection", + zap.String("reason", reason), + zap.Stringer("client_addr", conn.RemoteAddr()), + zap.Error(conn.Close())) + return true } - metrics.RejectConnCounter.WithLabelValues("memory").Inc() - s.logger.Warn("reject connection due to high memory usage", - zap.Stringer("client_addr", conn.RemoteAddr()), - zap.Float64("threshold", threshold), - zap.Float64("usage", snapshot.Usage), - zap.Uint64("used", snapshot.Used), - zap.Uint64("limit", snapshot.Limit), - zap.Time("last_update", snapshot.UpdateTime), - zap.Error(conn.Close())) - return true + return false } func (s *SQLServer) PreClose() { diff --git a/pkg/proxy/proxy_test.go b/pkg/proxy/proxy_test.go index 2dda7957c..43845d70f 100644 --- a/pkg/proxy/proxy_test.go +++ b/pkg/proxy/proxy_test.go @@ -21,7 +21,6 @@ import ( "github.com/pingcap/tiproxy/pkg/balance/router" "github.com/pingcap/tiproxy/pkg/manager/cert" "github.com/pingcap/tiproxy/pkg/manager/id" - mgrmem "github.com/pingcap/tiproxy/pkg/manager/memory" "github.com/pingcap/tiproxy/pkg/metrics" "github.com/pingcap/tiproxy/pkg/proxy/backend" "github.com/pingcap/tiproxy/pkg/proxy/client" @@ -34,7 +33,7 @@ func TestCreateConn(t *testing.T) { cfg := &config.Config{} certManager := cert.NewCertManager() require.NoError(t, certManager.Init(cfg, lg, nil)) - server, err := NewSQLServer(lg, cfg, certManager, id.NewIDManager(), nil, &mockHsHandler{}, nil) + server, err := NewSQLServer(lg, cfg, certManager, id.NewIDManager(), nil, &mockHsHandler{}, nil, nil) require.NoError(t, err) server.Run(context.Background(), nil) defer func() { @@ -75,17 +74,8 @@ func TestRejectConnByMemory(t *testing.T) { lg, _ := logger.CreateLoggerForTest(t) certManager := cert.NewCertManager() require.NoError(t, certManager.Init(&config.Config{}, lg, nil)) - server, err := NewSQLServer(lg, &config.Config{}, certManager, id.NewIDManager(), nil, &mockHsHandler{}, &mockMemUsageProvider{ - reject: true, - snapshot: mgrmem.UsageSnapshot{ - Used: 9 * (1 << 30), - Limit: 10 * (1 << 30), - Usage: 0.9, - UpdateTime: time.Now(), - Valid: true, - }, - threshold: 0.9, - }) + memUsage := &mockMemUsageProvider{reject: true} + server, err := NewSQLServer(lg, &config.Config{}, certManager, id.NewIDManager(), nil, &mockHsHandler{}, memUsage, memUsage) require.NoError(t, err) server.Run(context.Background(), nil) defer func() { @@ -135,7 +125,7 @@ func TestTrackConnBufferMemDelta(t *testing.T) { } require.NoError(t, certManager.Init(cfg, lg, nil)) memUsage := &mockMemUsageProvider{} - server, err := NewSQLServer(lg, cfg, certManager, id.NewIDManager(), nil, &mockHsHandler{}, memUsage) + server, err := NewSQLServer(lg, cfg, certManager, id.NewIDManager(), nil, &mockHsHandler{}, memUsage, memUsage) require.NoError(t, err) server.Run(context.Background(), nil) defer func() { @@ -168,7 +158,7 @@ func TestGracefulCloseConn(t *testing.T) { }, }, } - server, err := NewSQLServer(lg, cfg, nil, id.NewIDManager(), nil, hsHandler, nil) + server, err := NewSQLServer(lg, cfg, nil, id.NewIDManager(), nil, hsHandler, nil, nil) require.NoError(t, err) finish := make(chan struct{}) go func() { @@ -198,7 +188,7 @@ func TestGracefulCloseConn(t *testing.T) { } // Graceful shutdown will be blocked if there are alive connections. - server, err = NewSQLServer(lg, cfg, nil, id.NewIDManager(), nil, hsHandler, nil) + server, err = NewSQLServer(lg, cfg, nil, id.NewIDManager(), nil, hsHandler, nil, nil) require.NoError(t, err) clientConn := createClientConn() go func() { @@ -224,7 +214,7 @@ func TestGracefulCloseConn(t *testing.T) { // Graceful shutdown will shut down after GracefulCloseConnTimeout. cfg.Proxy.GracefulCloseConnTimeout = 1 - server, err = NewSQLServer(lg, cfg, nil, id.NewIDManager(), nil, hsHandler, nil) + server, err = NewSQLServer(lg, cfg, nil, id.NewIDManager(), nil, hsHandler, nil, nil) require.NoError(t, err) createClientConn() go func() { @@ -252,7 +242,7 @@ func TestGracefulShutDown(t *testing.T) { }, }, } - server, err := NewSQLServer(lg, cfg, certManager, id.NewIDManager(), nil, &mockHsHandler{}, nil) + server, err := NewSQLServer(lg, cfg, certManager, id.NewIDManager(), nil, &mockHsHandler{}, nil, nil) require.NoError(t, err) server.Run(context.Background(), nil) @@ -290,7 +280,7 @@ func TestMultiAddr(t *testing.T) { Proxy: config.ProxyServer{ Addr: "0.0.0.0:0,0.0.0.0:0", }, - }, certManager, id.NewIDManager(), nil, &mockHsHandler{}, nil) + }, certManager, id.NewIDManager(), nil, &mockHsHandler{}, nil, nil) require.NoError(t, err) server.Run(context.Background(), nil) @@ -310,7 +300,7 @@ func TestWatchCfg(t *testing.T) { lg, _ := logger.CreateLoggerForTest(t) hsHandler := backend.NewDefaultHandshakeHandler(nil) cfgch := make(chan *config.Config) - server, err := NewSQLServer(lg, &config.Config{}, nil, id.NewIDManager(), nil, hsHandler, nil) + server, err := NewSQLServer(lg, &config.Config{}, nil, id.NewIDManager(), nil, hsHandler, nil, nil) require.NoError(t, err) server.Run(context.Background(), cfgch) cfg := &config.Config{ @@ -352,7 +342,7 @@ func TestRecoverPanic(t *testing.T) { } return nil }, - }, nil) + }, nil, nil) require.NoError(t, err) server.Run(context.Background(), nil) @@ -380,13 +370,14 @@ type mockHsHandler struct { type mockMemUsageProvider struct { reject bool - snapshot mgrmem.UsageSnapshot - threshold float64 connBufferMemDelta atomic.Int64 } -func (m *mockMemUsageProvider) ShouldRejectNewConn() (bool, mgrmem.UsageSnapshot, float64) { - return m.reject, m.snapshot, m.threshold +func (m *mockMemUsageProvider) RejectConns() (bool, string) { + if m.reject { + return true, "high memory usage" + } + return false, "" } func (m *mockMemUsageProvider) UpdateConnBufferMemory(delta int64) { diff --git a/pkg/server/api/debug.go b/pkg/server/api/debug.go index 27cc4fbd0..df8cf248c 100644 --- a/pkg/server/api/debug.go +++ b/pkg/server/api/debug.go @@ -13,12 +13,14 @@ import ( func (h *Server) DebugHealth(c *gin.Context) { status := http.StatusOK - if h.isClosing.Load() || !h.mgr.NsMgr.Ready() { + health := config.HealthInfo{ + ConfigChecksum: h.mgr.CfgMgr.GetConfigChecksum(), + } + if healthy, reason := h.mgr.Health.Healthy(); !healthy { status = http.StatusBadGateway + health.UnhealthyReason = reason } - c.JSON(status, config.HealthInfo{ - ConfigChecksum: h.mgr.CfgMgr.GetConfigChecksum(), - }) + c.JSON(status, health) } func (h *Server) DebugRedirect(c *gin.Context) { diff --git a/pkg/server/api/debug_test.go b/pkg/server/api/debug_test.go index 006739d92..a82d73a2b 100644 --- a/pkg/server/api/debug_test.go +++ b/pkg/server/api/debug_test.go @@ -4,9 +4,11 @@ package api import ( + "encoding/json" "net/http" "testing" + "github.com/pingcap/tiproxy/pkg/manager/health" "github.com/stretchr/testify/require" ) @@ -34,8 +36,11 @@ func TestDebug(t *testing.T) { require.Equal(t, http.StatusOK, r.StatusCode) }) - server.PreClose() + server.mgr.Health.(*health.Manager).PreClose() doHTTP(t, http.MethodGet, "/api/debug/health", httpOpts{}, func(t *testing.T, r *http.Response) { require.Equal(t, http.StatusBadGateway, r.StatusCode) + var health map[string]any + require.NoError(t, json.NewDecoder(r.Body).Decode(&health)) + require.Equal(t, "server is shutting down", health["unhealthy_reason"]) }) } diff --git a/pkg/server/api/server.go b/pkg/server/api/server.go index dae355848..5d8cd402f 100644 --- a/pkg/server/api/server.go +++ b/pkg/server/api/server.go @@ -42,23 +42,30 @@ type HTTPHandler interface { RegisterHTTP(c *gin.Engine) error } +// healthChecker reports whether the instance is serving. It is satisfied by +// *health.Manager; defined here so the api package does not depend on the health +// package. +type healthChecker interface { + Healthy() (bool, string) +} + type Managers struct { CfgMgr *mgrcfg.ConfigManager NsMgr mgrns.NamespaceManager CertMgr *mgrcrt.CertManager BackendReader BackendReader ReplayJobMgr mgrrp.JobManager + Health healthChecker } type Server struct { - listener net.Listener - wg waitgroup.WaitGroup - limit ratelimit.Limiter - ready *atomic.Bool - lg *zap.Logger - grpc *grpc.Server - isClosing atomic.Bool - mgr Managers + listener net.Listener + wg waitgroup.WaitGroup + limit ratelimit.Limiter + ready *atomic.Bool + lg *zap.Logger + grpc *grpc.Server + mgr Managers } func NewServer(cfg config.API, lg *zap.Logger, mgr Managers, handler HTTPHandler, ready *atomic.Bool) (*Server, error) { @@ -201,10 +208,6 @@ func (h *Server) registerAPI(g *gin.RouterGroup) { h.registerTraffic(g.Group("traffic")) } -func (h *Server) PreClose() { - h.isClosing.Store(true) -} - func (h *Server) Close() error { err := h.listener.Close() h.wg.Wait() diff --git a/pkg/server/api/server_test.go b/pkg/server/api/server_test.go index 6a1cfead2..5c531a92c 100644 --- a/pkg/server/api/server_test.go +++ b/pkg/server/api/server_test.go @@ -14,6 +14,7 @@ import ( "github.com/pingcap/tiproxy/lib/util/logger" mgrcrt "github.com/pingcap/tiproxy/pkg/manager/cert" mgrcfg "github.com/pingcap/tiproxy/pkg/manager/config" + "github.com/pingcap/tiproxy/pkg/manager/health" "github.com/stretchr/testify/require" "go.uber.org/atomic" "google.golang.org/grpc" @@ -33,6 +34,7 @@ func createServer(t *testing.T) (*Server, func(t *testing.T, method string, path crtmgr := mgrcrt.NewCertManager() require.NoError(t, crtmgr.Init(cfgmgr.GetConfig(), lg, cfgmgr.WatchConfig())) nsMgr := newMockNamespaceManager() + healthMgr := health.NewManager(nsMgr.Ready, func() (bool, string) { return false, "" }) srv, err := NewServer(config.API{ Addr: "0.0.0.0:0", }, lg, Managers{ @@ -41,6 +43,7 @@ func createServer(t *testing.T) (*Server, func(t *testing.T, method string, path CertMgr: crtmgr, BackendReader: &mockBackendReader{}, ReplayJobMgr: &mockReplayJobManager{}, + Health: healthMgr, }, nil, ready) require.NoError(t, err) t.Cleanup(func() { diff --git a/pkg/server/server.go b/pkg/server/server.go index 8a7ac2a61..88e54f7f8 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -5,6 +5,7 @@ package server import ( "context" + "fmt" "reflect" "runtime" @@ -13,6 +14,7 @@ import ( "github.com/pingcap/tiproxy/pkg/balance/metricsreader" "github.com/pingcap/tiproxy/pkg/manager/cert" mgrcfg "github.com/pingcap/tiproxy/pkg/manager/config" + "github.com/pingcap/tiproxy/pkg/manager/health" "github.com/pingcap/tiproxy/pkg/manager/id" "github.com/pingcap/tiproxy/pkg/manager/infosync" "github.com/pingcap/tiproxy/pkg/manager/logger" @@ -47,6 +49,7 @@ type Server struct { metricsReader metricsreader.MetricsReader replay mgrrp.JobManager memManager *memory.MemManager + healthMgr *health.Manager // etcd client etcdCli *clientv3.Client // HTTP client @@ -100,6 +103,21 @@ func NewServer(ctx context.Context, sctx *sctx.Context) (srv *Server, err error) srv.memManager = memory.NewMemManager(lg, srv.configManager) srv.memManager.Start(ctx) + // Aggregate the serving/accepting signals so DebugHealth and the proxy share + // one source of truth. The proxy starts listening before the namespace manager + // is ready, so RejectConns intentionally ignores the init phase. + srv.healthMgr = health.NewManager( + srv.namespaceManager.Ready, + func() (bool, string) { + reject, snapshot, threshold := srv.memManager.ShouldRejectNewConn() + if !reject { + return false, "" + } + return true, fmt.Sprintf("high memory usage (usage=%.4f, threshold=%.4f, used=%d, limit=%d, last_update=%s)", + snapshot.Usage, threshold, snapshot.Used, snapshot.Limit, snapshot.UpdateTime.String()) + }, + ) + // setup certs if err = srv.certManager.Init(cfg, lg.Named("cert"), srv.configManager.WatchConfig()); err != nil { return @@ -176,7 +194,7 @@ func NewServer(ctx context.Context, sctx *sctx.Context) (srv *Server, err error) // setup proxy server { - srv.proxy, err = proxy.NewSQLServer(lg.Named("proxy"), cfg, srv.certManager, idMgr, srv.replay.GetCapture(), hsHandler, srv.memManager) + srv.proxy, err = proxy.NewSQLServer(lg.Named("proxy"), cfg, srv.certManager, idMgr, srv.replay.GetCapture(), hsHandler, srv.memManager, srv.healthMgr) if err != nil { return } @@ -190,6 +208,7 @@ func NewServer(ctx context.Context, sctx *sctx.Context) (srv *Server, err error) CertMgr: srv.certManager, BackendReader: srv.metricsReader, ReplayJobMgr: srv.replay, + Health: srv.healthMgr, } if srv.apiServer, err = api.NewServer(cfg.API, lg.Named("api"), mgrs, handler, ready); err != nil { return @@ -231,8 +250,8 @@ func (s *Server) preClose() { s.vipManager.PreClose() } // Make the API server return unhealth. - if s.apiServer != nil { - s.apiServer.PreClose() + if s.healthMgr != nil { + s.healthMgr.PreClose() } // Resign the metric reader owner to make other members campaign ASAP. if s.metricsReader != nil && !reflect.ValueOf(s.metricsReader).IsNil() {