diff --git a/go.mod b/go.mod index e438228a7283..782a34f5e797 100644 --- a/go.mod +++ b/go.mod @@ -209,3 +209,4 @@ replace google.golang.org/grpc v1.59.0 => google.golang.org/grpc v1.26.0 // kvproto at the same time. You can run `go mod tidy` to make it replaced with go-mod style specification. // After the PR to kvproto is merged, remember to comment this out and run `go mod tidy`. // replace github.com/pingcap/kvproto => github.com/$YourPrivateRepo $YourPrivateBranch +replace github.com/pingcap/tidb-dashboard => github.com/HuSharp/tidb-dashboard v0.0.0-20240130065036-5d37687b904c diff --git a/go.sum b/go.sum index 0e308d173a0e..990fb8e16175 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/AlekSi/gocov-xml v1.0.0 h1:4QctJBgXEkbzeKz6PJy6bt3JSPNSN4I2mITYW+eKUo github.com/AlekSi/gocov-xml v1.0.0/go.mod h1:J0qYeZ6tDg4oZubW9mAAgxlqw39PDfoEkzB3HXSbEuA= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/HuSharp/tidb-dashboard v0.0.0-20240130065036-5d37687b904c h1:0w4RVGS3RFAKYyfhf8UpaPQz9YFG9VxEeCQ+n4ezypc= +github.com/HuSharp/tidb-dashboard v0.0.0-20240130065036-5d37687b904c/go.mod h1:ucZBRz52icb23T/5Z4CsuUHmarYiin7p2MeiVBe+o8c= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -440,8 +442,6 @@ github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3 h1:HR/ylkkLmGdSSDaD8 github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4= github.com/pingcap/sysutil v1.0.1-0.20230407040306-fb007c5aff21 h1:QV6jqlfOkh8hqvEAgwBZa+4bSgO0EeKC7s5c6Luam2I= github.com/pingcap/sysutil v1.0.1-0.20230407040306-fb007c5aff21/go.mod h1:QYnjfA95ZaMefyl1NO8oPtKeb8pYUdnDVhQgf+qdpjM= -github.com/pingcap/tidb-dashboard v0.0.0-20240111062855-41f7c8011953 h1:vY/bY5vkSvvuXB1030AUmy0LFhuEA53ryVdF/bTbFXU= -github.com/pingcap/tidb-dashboard v0.0.0-20240111062855-41f7c8011953/go.mod h1:ucZBRz52icb23T/5Z4CsuUHmarYiin7p2MeiVBe+o8c= github.com/pingcap/tipb v0.0.0-20220718022156-3e2483c20a9e h1:FBaTXU8C3xgt/drM58VHxojHo/QoG1oPsgWTGvaSpO4= github.com/pingcap/tipb v0.0.0-20220718022156-3e2483c20a9e/go.mod h1:A7mrd7WHBl1o63LE2bIBGEJMTNWXqhgmYiOvMLxozfs= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= diff --git a/pkg/utils/etcdutil/etcdutil.go b/pkg/utils/etcdutil/etcdutil.go index d56d0e662e37..a36ece4aafa5 100644 --- a/pkg/utils/etcdutil/etcdutil.go +++ b/pkg/utils/etcdutil/etcdutil.go @@ -86,7 +86,8 @@ func CheckClusterID(localClusterID types.ID, um types.URLsMap, tlsConfig *tls.Co for _, u := range peerURLs { trp := &http.Transport{ - TLSClientConfig: tlsConfig, + TLSClientConfig: tlsConfig, + DisableKeepAlives: true, } remoteCluster, gerr := etcdserver.GetClusterFromRemotePeers(nil, []string{u}, trp) trp.CloseIdleConnections() diff --git a/pkg/utils/etcdutil/etcdutil_test.go b/pkg/utils/etcdutil/etcdutil_test.go index 8fb8bc59f880..b2043a17f735 100644 --- a/pkg/utils/etcdutil/etcdutil_test.go +++ b/pkg/utils/etcdutil/etcdutil_test.go @@ -39,14 +39,14 @@ import ( "go.etcd.io/etcd/etcdserver/etcdserverpb" "go.etcd.io/etcd/mvcc/mvccpb" "go.etcd.io/etcd/pkg/types" - "go.uber.org/goleak" ) func TestMain(m *testing.M) { - goleak.VerifyTestMain(m, testutil.LeakOptions...) + testutil.MustTestMainWithLeakDetection(m) } func TestMemberHelpers(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) servers, client1, clean := NewTestEtcdCluster(t, 1) defer clean() @@ -81,6 +81,7 @@ func TestMemberHelpers(t *testing.T) { } func TestEtcdKVGet(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) _, client, clean := NewTestEtcdCluster(t, 1) defer clean() @@ -119,6 +120,7 @@ func TestEtcdKVGet(t *testing.T) { } func TestEtcdKVPutWithTTL(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) _, client, clean := NewTestEtcdCluster(t, 1) defer clean() @@ -147,6 +149,7 @@ func TestEtcdKVPutWithTTL(t *testing.T) { } func TestInitClusterID(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) _, client, clean := NewTestEtcdCluster(t, 1) defer clean() @@ -166,6 +169,7 @@ func TestInitClusterID(t *testing.T) { } func TestEtcdClientSync(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) re.NoError(failpoint.Enable("github.com/tikv/pd/pkg/utils/etcdutil/fastTick", "return(true)")) @@ -204,6 +208,7 @@ func checkEtcdClientHealth(re *require.Assertions, client *clientv3.Client) { } func TestEtcdScaleInAndOut(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) // Start a etcd server. servers, _, clean := NewTestEtcdCluster(t, 1) @@ -230,6 +235,7 @@ func TestEtcdScaleInAndOut(t *testing.T) { } func TestRandomKillEtcd(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) re.NoError(failpoint.Enable("github.com/tikv/pd/pkg/utils/etcdutil/fastTick", "return(true)")) // Start a etcd server. @@ -260,6 +266,7 @@ func TestRandomKillEtcd(t *testing.T) { } func TestEtcdWithHangLeaderEnableCheck(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) var err error // Test with enable check. @@ -285,9 +292,11 @@ func checkEtcdWithHangLeader(t *testing.T) error { // Create a proxy to etcd1. proxyAddr := tempurl.Alloc() var enableDiscard atomic.Bool - go proxyWithDiscard(re, cfg1.LCUrls[0].String(), proxyAddr, &enableDiscard) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go proxyWithDiscard(ctx, re, cfg1.LCUrls[0].String(), proxyAddr, &enableDiscard) - // Create a etcd client with etcd1 as endpoint. + // Create an etcd client with etcd1 as endpoint. urls, err := types.NewURLs([]string{proxyAddr}) re.NoError(err) client1, err := CreateEtcdClient(nil, urls) @@ -307,30 +316,47 @@ func checkEtcdWithHangLeader(t *testing.T) error { return err } -func proxyWithDiscard(re *require.Assertions, server, proxy string, enableDiscard *atomic.Bool) { +func proxyWithDiscard(ctx context.Context, re *require.Assertions, server, proxy string, enableDiscard *atomic.Bool) { server = strings.TrimPrefix(server, "http://") proxy = strings.TrimPrefix(proxy, "http://") l, err := net.Listen("tcp", proxy) re.NoError(err) + defer l.Close() for { - connect, err := l.Accept() - re.NoError(err) - go func(connect net.Conn) { - serverConnect, err := net.Dial("tcp", server) - re.NoError(err) - pipe(connect, serverConnect, enableDiscard) - }(connect) + type accepted struct { + conn net.Conn + err error + } + accept := make(chan accepted, 1) + go func() { + conn, err := l.Accept() + accept <- accepted{conn, err} + }() + + select { + case <-ctx.Done(): + return + case a := <-accept: + if a.err != nil { + return + } + go func(connect net.Conn) { + serverConnect, err := net.Dial("tcp", server) + re.NoError(err) + pipe(ctx, connect, serverConnect, enableDiscard) + }(a.conn) + } } } -func pipe(src net.Conn, dst net.Conn, enableDiscard *atomic.Bool) { +func pipe(ctx context.Context, src net.Conn, dst net.Conn, enableDiscard *atomic.Bool) { errChan := make(chan error, 1) go func() { - err := ioCopy(src, dst, enableDiscard) + err := ioCopy(ctx, src, dst, enableDiscard) errChan <- err }() go func() { - err := ioCopy(dst, src, enableDiscard) + err := ioCopy(ctx, dst, src, enableDiscard) errChan <- err }() <-errChan @@ -338,28 +364,31 @@ func pipe(src net.Conn, dst net.Conn, enableDiscard *atomic.Bool) { src.Close() } -func ioCopy(dst io.Writer, src io.Reader, enableDiscard *atomic.Bool) (err error) { +func ioCopy(ctx context.Context, dst io.Writer, src io.Reader, enableDiscard *atomic.Bool) (err error) { buffer := make([]byte, 32*1024) for { - if enableDiscard.Load() { - io.Copy(io.Discard, src) - } - readNum, errRead := src.Read(buffer) - if readNum > 0 { - writeNum, errWrite := dst.Write(buffer[:readNum]) - if errWrite != nil { - return errWrite + select { + case <-ctx.Done(): + return nil + default: + if enableDiscard.Load() { + io.Copy(io.Discard, src) } - if readNum != writeNum { - return io.ErrShortWrite + readNum, errRead := src.Read(buffer) + if readNum > 0 { + writeNum, errWrite := dst.Write(buffer[:readNum]) + if errWrite != nil { + return errWrite + } + if readNum != writeNum { + return io.ErrShortWrite + } + } + if errRead != nil { + return err } - } - if errRead != nil { - err = errRead - break } } - return err } type loopWatcherTestSuite struct { @@ -374,6 +403,7 @@ type loopWatcherTestSuite struct { } func TestLoopWatcherTestSuite(t *testing.T) { + testutil.RegisterLeakDetection(t) suite.Run(t, new(loopWatcherTestSuite)) } diff --git a/pkg/utils/etcdutil/health_checker_test.go b/pkg/utils/etcdutil/health_checker_test.go index 3891fbfce7a5..a728f822725e 100644 --- a/pkg/utils/etcdutil/health_checker_test.go +++ b/pkg/utils/etcdutil/health_checker_test.go @@ -29,6 +29,7 @@ type testCase struct { func check(re *require.Assertions, testCases []*testCase) { checker := &healthChecker{} + defer checker.close() lastEps := []string{} for idx, tc := range testCases { // Send the health probes to the channel. diff --git a/pkg/utils/etcdutil/testutil.go b/pkg/utils/etcdutil/testutil.go index 57f7200ecb80..6321872618b4 100644 --- a/pkg/utils/etcdutil/testutil.go +++ b/pkg/utils/etcdutil/testutil.go @@ -54,7 +54,7 @@ func genRandName() string { return "test_etcd_" + strconv.FormatInt(time.Now().UnixNano()%10000, 10) } -// NewTestEtcdCluster is used to create a etcd cluster for the unit test purpose. +// NewTestEtcdCluster is used to create an etcd cluster for the unit test purpose. func NewTestEtcdCluster(t *testing.T, count int) (servers []*embed.Etcd, etcdClient *clientv3.Client, clean func()) { re := require.New(t) servers = make([]*embed.Etcd, 0, count) diff --git a/pkg/utils/testutil/leak.go b/pkg/utils/testutil/leak.go index d1329aef0e60..52cf46cc0f6b 100644 --- a/pkg/utils/testutil/leak.go +++ b/pkg/utils/testutil/leak.go @@ -14,7 +14,29 @@ package testutil -import "go.uber.org/goleak" +import ( + "fmt" + "net/http" + "os" + "regexp" + "runtime" + "sort" + "strings" + "testing" + "time" + + "go.uber.org/goleak" +) + +/* +support 2 ways to use this package: +1. use LeakOptions to filter the goroutines. +- which is a global variable, so it's not fine-grained, maybe hide some goroutines. +2. use RegisterLeakDetection to register before-and-after code to a test. +- can be more fine-grained than LeakOptions +- we need to add ignore options to interestingGoroutines() if we want to ignore some goroutines +(but we need to give a reason why we ignore it) +*/ // LeakOptions is used to filter the goroutines. var LeakOptions = []goleak.Option{ @@ -23,9 +45,132 @@ var LeakOptions = []goleak.Option{ goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), goleak.IgnoreTopFunction("go.etcd.io/etcd/pkg/logutil.(*MergeLogger).outputLoop"), goleak.IgnoreTopFunction("sync.runtime_notifyListWait"), - // TODO: remove the below options once we fixed the http connection leak problems - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), // natefinch/lumberjack#56, It's a goroutine leak bug. Another ignore option PR https://github.com/pingcap/tidb/pull/27405/ goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), } + +/* +CheckLeakedGoroutine verifies tests do not leave any leaky +goroutines. It returns true when there are goroutines still +running(leaking) after all tests. + + func TestMain(m *testing.M) { + testutil.MustTestMainWithLeakDetection(m) + } + + func TestSample(t *testing.T) { + testutil.RegisterLeakDetection(t) + ... + } +*/ +func CheckLeakedGoroutine() bool { + gs := interestingGoroutines() + if len(gs) == 0 { + return false + } + + stackCount := make(map[string]int) + re := regexp.MustCompile(`\(0[0-9a-fx, ]*\)`) + for _, g := range gs { + // strip out pointer arguments in first function of stack dump + normalized := string(re.ReplaceAll([]byte(g), []byte("(...)"))) + stackCount[normalized]++ + } + + fmt.Fprint(os.Stderr, "Unexpected goroutines running after all test(s).\n") + for stack, count := range stackCount { + fmt.Fprintf(os.Stderr, "%d instances of:\n%s\n", count, stack) + } + return true +} + +// MustTestMainWithLeakDetection expands standard m.Run with leaked +// goroutines detection. +func MustTestMainWithLeakDetection(m *testing.M) { + v := m.Run() + if v == 0 { + MustCheckLeakedGoroutine() + } + os.Exit(v) +} + +// CheckAfterTest returns an error if AfterTest would fail with an error. +// Waits for go-routines shutdown for 'd'. +func CheckAfterTest(d time.Duration) error { + http.DefaultTransport.(*http.Transport).CloseIdleConnections() + + var stacks string + begin := time.Now() + for time.Since(begin) < d { + goroutines := interestingGoroutines() + if len(goroutines) == 0 { + return nil + } + stacks = strings.Join(goroutines, "\n\n") + + // Undesired goroutines found, but goroutines might just still be + // shutting down, so give it some time. + runtime.Gosched() + time.Sleep(50 * time.Millisecond) + } + return fmt.Errorf("appears to have leaked %s", stacks) +} + +// RegisterLeakDetection is a convenient way to register before-and-after code to a test. +// If you execute RegisterLeakDetection, you don't need to explicitly register AfterTest. +func RegisterLeakDetection(t *testing.T) { + if err := CheckAfterTest(10 * time.Millisecond); err != nil { + t.Skip("Found leaked goroutined BEFORE test", err) + return + } + t.Cleanup(func() { + // If test-failed the leaked goroutines list is hidding the real + // source of problem. + if err := CheckAfterTest(1 * time.Second); err != nil { + t.Errorf("Test %v", err) + } + }) +} + +func interestingGoroutines() (gs []string) { + buf := make([]byte, 2<<20) + buf = buf[:runtime.Stack(buf, true)] + for _, g := range strings.Split(string(buf), "\n\n") { + sl := strings.SplitN(g, "\n", 2) + if len(sl) != 2 { + continue + } + stack := strings.TrimSpace(sl[1]) + if stack == "" || + strings.Contains(stack, "interestingGoroutines") || + strings.Contains(stack, "google.golang.org/grpc.(*ccBalancerWrapper).watcher") || + // for TestAllocatorLeader + strings.Contains(stack, "google.golang.org/grpc.(*addrConn).resetTransport") || + strings.Contains(stack, "go.etcd.io/etcd/pkg/logutil.(*MergeLogger).outputLoop") || + strings.Contains(stack, "github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain") || + strings.Contains(stack, "sync.runtime_notifyListWait") || + strings.Contains(stack, "go.etcd.io/etcd/pkg/transport.timeoutConn.Read") || + strings.Contains(stack, "gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun") || + strings.Contains(stack, "writeLoop") || + strings.Contains(stack, "testing.(*T).Run") { + continue + } + gs = append(gs, stack) + } + sort.Strings(gs) + return gs +} + +// MustCheckLeakedGoroutine verifies tests do not leave any leaky +func MustCheckLeakedGoroutine() { + http.DefaultTransport.(*http.Transport).CloseIdleConnections() + + CheckAfterTest(1 * time.Second) + + // Let the other goroutines finalize. + runtime.Gosched() + + if CheckLeakedGoroutine() { + os.Exit(1) + } +} diff --git a/scripts/ci-subtask.sh b/scripts/ci-subtask.sh index 40f2863e1f9d..c6cb42a8cd20 100755 --- a/scripts/ci-subtask.sh +++ b/scripts/ci-subtask.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # ./ci-subtask.sh diff --git a/server/server_test.go b/server/server_test.go index 32f5d0646bc7..806376e948cf 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -32,11 +32,10 @@ import ( "github.com/tikv/pd/server/config" "go.etcd.io/etcd/embed" "go.etcd.io/etcd/pkg/types" - "go.uber.org/goleak" ) func TestMain(m *testing.M) { - goleak.VerifyTestMain(m, testutil.LeakOptions...) + testutil.MustTestMainWithLeakDetection(m) } type leaderServerTestSuite struct { @@ -46,9 +45,12 @@ type leaderServerTestSuite struct { cancel context.CancelFunc svrs map[string]*Server leaderPath string + + httpClient *http.Client } func TestLeaderServerTestSuite(t *testing.T) { + testutil.RegisterLeakDetection(t) suite.Run(t, new(leaderServerTestSuite)) } @@ -78,6 +80,11 @@ func (suite *leaderServerTestSuite) SetupSuite() { suite.svrs[svr.GetAddr()] = svr suite.leaderPath = svr.GetMember().GetLeaderPath() } + suite.httpClient = &http.Client{ + Transport: &http.Transport{ + DisableKeepAlives: true, + }, + } } func (suite *leaderServerTestSuite) TearDownSuite() { @@ -86,6 +93,7 @@ func (suite *leaderServerTestSuite) TearDownSuite() { svr.Close() testutil.CleanServer(svr.cfg.DataDir) } + suite.httpClient.CloseIdleConnections() } func (suite *leaderServerTestSuite) newTestServersWithCfgs( @@ -160,8 +168,13 @@ func (suite *leaderServerTestSuite) TestCheckClusterID() { } // Start another cluster. - _, cleanB := suite.newTestServersWithCfgs(ctx, []*config.Config{cfgB}, re) - defer cleanB() + servers, cleanB := suite.newTestServersWithCfgs(ctx, []*config.Config{cfgB}, re) + defer func() { + cleanB() + for _, svr := range servers { + svr.Close() + } + }() // Start previous cluster, expect an error. cfgA.InitialCluster = originInitial @@ -229,7 +242,7 @@ func (suite *leaderServerTestSuite) TestSourceIpForHeaderForwarded() { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/pd/apis/mock/v1/hello", svr.GetAddr()), http.NoBody) re.NoError(err) req.Header.Add(apiutil.XForwardedForHeader, "127.0.0.2") - resp, err := http.DefaultClient.Do(req) + resp, err := suite.httpClient.Do(req) re.NoError(err) re.Equal(http.StatusOK, resp.StatusCode) defer resp.Body.Close() @@ -260,7 +273,7 @@ func (suite *leaderServerTestSuite) TestSourceIpForHeaderXReal() { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/pd/apis/mock/v1/hello", svr.GetAddr()), http.NoBody) re.NoError(err) req.Header.Add(apiutil.XRealIPHeader, "127.0.0.2") - resp, err := http.DefaultClient.Do(req) + resp, err := suite.httpClient.Do(req) re.NoError(err) re.Equal(http.StatusOK, resp.StatusCode) defer resp.Body.Close() @@ -292,7 +305,7 @@ func (suite *leaderServerTestSuite) TestSourceIpForHeaderBoth() { re.NoError(err) req.Header.Add(apiutil.XForwardedForHeader, "127.0.0.2") req.Header.Add(apiutil.XRealIPHeader, "127.0.0.3") - resp, err := http.DefaultClient.Do(req) + resp, err := suite.httpClient.Do(req) re.NoError(err) re.Equal(http.StatusOK, resp.StatusCode) defer resp.Body.Close() diff --git a/tests/dashboard/service_test.go b/tests/dashboard/service_test.go index 478576293286..5cb8d1d93987 100644 --- a/tests/dashboard/service_test.go +++ b/tests/dashboard/service_test.go @@ -30,11 +30,10 @@ import ( "github.com/tikv/pd/pkg/utils/testutil" "github.com/tikv/pd/server/config" "github.com/tikv/pd/tests" - "go.uber.org/goleak" ) func TestMain(m *testing.M) { - goleak.VerifyTestMain(m, testutil.LeakOptions...) + testutil.MustTestMainWithLeakDetection(m) } type dashboardTestSuite struct { @@ -45,6 +44,7 @@ type dashboardTestSuite struct { } func TestDashboardTestSuite(t *testing.T) { + testutil.RegisterLeakDetection(t) suite.Run(t, new(dashboardTestSuite)) } @@ -59,6 +59,9 @@ func (suite *dashboardTestSuite) SetupSuite() { // unclosed. return http.ErrUseLastResponse }, + Transport: &http.Transport{ + DisableKeepAlives: true, + }, } } @@ -139,7 +142,7 @@ func (suite *dashboardTestSuite) testDashboard(re *require.Assertions, internalP // auto select node dashboardAddress1 := suite.checkServiceIsStarted(re, internalProxy, servers, leader) - // pd-ctl set another addr + // set another addr var dashboardAddress2 string for _, srv := range servers { if srv.GetAddr() != dashboardAddress1 { @@ -160,7 +163,7 @@ func (suite *dashboardTestSuite) testDashboard(re *require.Assertions, internalP suite.checkServiceIsStarted(re, internalProxy, servers, leader) re.Equal(dashboardAddress2, leader.GetServer().GetPersistOptions().GetDashboardAddress()) - // pd-ctl set stop + // set stop input = map[string]interface{}{ "dashboard-address": "none", } diff --git a/tests/integrations/go.mod b/tests/integrations/go.mod index 31d43cb86f63..62f70497845b 100644 --- a/tests/integrations/go.mod +++ b/tests/integrations/go.mod @@ -190,3 +190,5 @@ require ( gorm.io/driver/sqlite v1.4.3 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) + +replace github.com/pingcap/tidb-dashboard => github.com/HuSharp/tidb-dashboard v0.0.0-20240130065036-5d37687b904c diff --git a/tests/integrations/go.sum b/tests/integrations/go.sum index 556932a24480..e86f1b622d2d 100644 --- a/tests/integrations/go.sum +++ b/tests/integrations/go.sum @@ -4,6 +4,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/HuSharp/tidb-dashboard v0.0.0-20240130065036-5d37687b904c h1:0w4RVGS3RFAKYyfhf8UpaPQz9YFG9VxEeCQ+n4ezypc= +github.com/HuSharp/tidb-dashboard v0.0.0-20240130065036-5d37687b904c/go.mod h1:ucZBRz52icb23T/5Z4CsuUHmarYiin7p2MeiVBe+o8c= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -417,8 +419,6 @@ github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3 h1:HR/ylkkLmGdSSDaD8 github.com/pingcap/log v1.1.1-0.20221110025148-ca232912c9f3/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4= github.com/pingcap/sysutil v1.0.1-0.20230407040306-fb007c5aff21 h1:QV6jqlfOkh8hqvEAgwBZa+4bSgO0EeKC7s5c6Luam2I= github.com/pingcap/sysutil v1.0.1-0.20230407040306-fb007c5aff21/go.mod h1:QYnjfA95ZaMefyl1NO8oPtKeb8pYUdnDVhQgf+qdpjM= -github.com/pingcap/tidb-dashboard v0.0.0-20240111062855-41f7c8011953 h1:vY/bY5vkSvvuXB1030AUmy0LFhuEA53ryVdF/bTbFXU= -github.com/pingcap/tidb-dashboard v0.0.0-20240111062855-41f7c8011953/go.mod h1:ucZBRz52icb23T/5Z4CsuUHmarYiin7p2MeiVBe+o8c= github.com/pingcap/tipb v0.0.0-20220718022156-3e2483c20a9e h1:FBaTXU8C3xgt/drM58VHxojHo/QoG1oPsgWTGvaSpO4= github.com/pingcap/tipb v0.0.0-20220718022156-3e2483c20a9e/go.mod h1:A7mrd7WHBl1o63LE2bIBGEJMTNWXqhgmYiOvMLxozfs= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= diff --git a/tests/server/api/api_test.go b/tests/server/api/api_test.go index c80048b141f5..6a64d98fbdd4 100644 --- a/tests/server/api/api_test.go +++ b/tests/server/api/api_test.go @@ -40,14 +40,14 @@ import ( "github.com/tikv/pd/server/api" "github.com/tikv/pd/server/config" "github.com/tikv/pd/tests" - "go.uber.org/goleak" ) func TestMain(m *testing.M) { - goleak.VerifyTestMain(m, testutil.LeakOptions...) + testutil.MustTestMainWithLeakDetection(m) } func TestReconnect(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -114,6 +114,7 @@ type middlewareTestSuite struct { } func TestMiddlewareTestSuite(t *testing.T) { + testutil.RegisterLeakDetection(t) suite.Run(t, new(middlewareTestSuite)) } @@ -570,6 +571,7 @@ type redirectorTestSuite struct { } func TestRedirectorTestSuite(t *testing.T) { + testutil.RegisterLeakDetection(t) suite.Run(t, new(redirectorTestSuite)) } @@ -699,6 +701,7 @@ func mustRequestSuccess(re *require.Assertions, s *server.Server) http.Header { } func TestRemovingProgress(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) re.NoError(failpoint.Enable("github.com/tikv/pd/server/cluster/highFrequencyClusterJobs", `return(true)`)) ctx, cancel := context.WithCancel(context.Background()) @@ -817,6 +820,7 @@ func TestRemovingProgress(t *testing.T) { } func TestSendApiWhenRestartRaftCluster(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -858,6 +862,7 @@ func TestSendApiWhenRestartRaftCluster(t *testing.T) { } func TestPreparingProgress(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) re.NoError(failpoint.Enable("github.com/tikv/pd/server/cluster/highFrequencyClusterJobs", `return(true)`)) ctx, cancel := context.WithCancel(context.Background()) diff --git a/tests/server/member/member_test.go b/tests/server/member/member_test.go index d87e8a1a5c0a..083911472068 100644 --- a/tests/server/member/member_test.go +++ b/tests/server/member/member_test.go @@ -35,14 +35,14 @@ import ( "github.com/tikv/pd/server" "github.com/tikv/pd/server/config" "github.com/tikv/pd/tests" - "go.uber.org/goleak" ) func TestMain(m *testing.M) { - goleak.VerifyTestMain(m, testutil.LeakOptions...) + testutil.MustTestMainWithLeakDetection(m) } func TestMemberDelete(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -84,6 +84,7 @@ func TestMemberDelete(t *testing.T) { } httpClient := &http.Client{Timeout: 15 * time.Second} + defer httpClient.CloseIdleConnections() for _, table := range tables { t.Log(time.Now(), "try to delete:", table.path) testutil.Eventually(re, func() bool { @@ -148,6 +149,7 @@ func checkMemberList(re *require.Assertions, clientURL string, configs []*config } func TestLeaderPriority(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -204,6 +206,7 @@ func waitEtcdLeaderChange(re *require.Assertions, server *tests.TestServer, old } func TestLeaderResign(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -227,6 +230,7 @@ func TestLeaderResign(t *testing.T) { } func TestLeaderResignWithBlock(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -249,6 +253,7 @@ func TestLeaderResignWithBlock(t *testing.T) { } func TestPDLeaderLostWhileEtcdLeaderIntact(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -285,6 +290,7 @@ func waitLeaderChange(re *require.Assertions, cluster *tests.TestCluster, old st } func TestMoveLeader(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -324,6 +330,7 @@ func TestMoveLeader(t *testing.T) { } func TestCampaignLeaderFrequently(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -347,6 +354,7 @@ func TestCampaignLeaderFrequently(t *testing.T) { } func TestGrantLeaseFailed(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -372,6 +380,7 @@ func TestGrantLeaseFailed(t *testing.T) { } func TestGetLeader(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/tests/server/server_test.go b/tests/server/server_test.go index 3b85cd3cf0d6..1bcc640f6467 100644 --- a/tests/server/server_test.go +++ b/tests/server/server_test.go @@ -23,14 +23,14 @@ import ( "github.com/tikv/pd/pkg/utils/testutil" "github.com/tikv/pd/server/config" "github.com/tikv/pd/tests" - "go.uber.org/goleak" ) func TestMain(m *testing.M) { - goleak.VerifyTestMain(m, testutil.LeakOptions...) + testutil.MustTestMainWithLeakDetection(m) } func TestUpdateAdvertiseUrls(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -72,6 +72,7 @@ func TestUpdateAdvertiseUrls(t *testing.T) { } func TestClusterID(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -108,6 +109,7 @@ func TestClusterID(t *testing.T) { } func TestLeader(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/tests/server/tso/allocator_test.go b/tests/server/tso/allocator_test.go index 41f544729c29..fff87da0c710 100644 --- a/tests/server/tso/allocator_test.go +++ b/tests/server/tso/allocator_test.go @@ -35,6 +35,7 @@ import ( ) func TestAllocatorLeader(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -109,6 +110,7 @@ func TestAllocatorLeader(t *testing.T) { } func TestPriorityAndDifferentLocalTSO(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/tests/server/tso/common_test.go b/tests/server/tso/common_test.go index 877fcb109826..6f65e80a4bd4 100644 --- a/tests/server/tso/common_test.go +++ b/tests/server/tso/common_test.go @@ -24,7 +24,6 @@ import ( "github.com/pingcap/kvproto/pkg/pdpb" "github.com/stretchr/testify/require" "github.com/tikv/pd/pkg/utils/testutil" - "go.uber.org/goleak" ) const ( @@ -52,5 +51,5 @@ func testGetTimestamp(re *require.Assertions, ctx context.Context, pdCli pdpb.PD } func TestMain(m *testing.M) { - goleak.VerifyTestMain(m, testutil.LeakOptions...) + testutil.MustTestMainWithLeakDetection(m) } diff --git a/tests/server/tso/consistency_test.go b/tests/server/tso/consistency_test.go index d1c45df7f17e..a0d671221235 100644 --- a/tests/server/tso/consistency_test.go +++ b/tests/server/tso/consistency_test.go @@ -49,6 +49,7 @@ type tsoConsistencyTestSuite struct { } func TestTSOConsistencyTestSuite(t *testing.T) { + testutil.RegisterLeakDetection(t) suite.Run(t, new(tsoConsistencyTestSuite)) } diff --git a/tests/server/tso/global_tso_test.go b/tests/server/tso/global_tso_test.go index 5ae2e6e0f675..a40bfb6bc900 100644 --- a/tests/server/tso/global_tso_test.go +++ b/tests/server/tso/global_tso_test.go @@ -43,6 +43,7 @@ import ( // leaders. func TestRequestFollower(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -87,6 +88,7 @@ func TestRequestFollower(t *testing.T) { // In some cases, when a TSO request arrives, the SyncTimestamp may not finish yet. // This test is used to simulate this situation and verify that the retry mechanism. func TestDelaySyncTimestamp(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -132,6 +134,7 @@ func TestDelaySyncTimestamp(t *testing.T) { } func TestLogicalOverflow(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) runCase := func(updateInterval time.Duration) { diff --git a/tests/server/tso/manager_test.go b/tests/server/tso/manager_test.go index c4f5aa5e3e38..0b22356ef9b2 100644 --- a/tests/server/tso/manager_test.go +++ b/tests/server/tso/manager_test.go @@ -36,6 +36,7 @@ import ( // TestClusterDCLocations will write different dc-locations to each server // and test whether we can get the whole dc-location config from each server. func TestClusterDCLocations(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -88,6 +89,7 @@ func TestClusterDCLocations(t *testing.T) { } func TestLocalTSOSuffix(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -142,6 +144,7 @@ func TestLocalTSOSuffix(t *testing.T) { } func TestNextLeaderKey(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/tests/server/tso/tso_test.go b/tests/server/tso/tso_test.go index 9eff1192e570..a380cfa81378 100644 --- a/tests/server/tso/tso_test.go +++ b/tests/server/tso/tso_test.go @@ -32,6 +32,7 @@ import ( ) func TestLoadTimestamp(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -96,6 +97,7 @@ func requestLocalTSOs(re *require.Assertions, cluster *tests.TestCluster, dcLoca } func TestDisableLocalTSOAfterEnabling(t *testing.T) { + testutil.RegisterLeakDetection(t) re := require.New(t) ctx, cancel := context.WithCancel(context.Background()) defer cancel()