diff --git a/client/http/interface.go b/client/http/interface.go index f90ab19624f..cd9fc22702e 100644 --- a/client/http/interface.go +++ b/client/http/interface.go @@ -715,7 +715,7 @@ func (c *client) GetRegionLabelRulesByIDs(ctx context.Context, ruleIDs []string) var labelRules []*LabelRule err = c.request(ctx, newRequestInfo(). WithName(getRegionLabelRulesByIDsName). - WithURI(RegionLabelRules). + WithURI(RegionLabelRulesByIDs). WithMethod(http.MethodGet). WithBody(idsJSON). WithResp(&labelRules)) diff --git a/pkg/core/region.go b/pkg/core/region.go index 9768a258889..53268589c8a 100644 --- a/pkg/core/region.go +++ b/pkg/core/region.go @@ -2143,7 +2143,7 @@ func HexRegionKey(key []byte) []byte { // HexRegionKeyStr converts region key to hex format. Used for formatting region in // logs. func HexRegionKeyStr(key []byte) string { - return typeutil.BytesToString(HexRegionKey(key)) + return string(HexRegionKey(key)) } // RegionToHexMeta converts a region meta's keys to hex format. Used for formatting diff --git a/pkg/encryption/crypter.go b/pkg/encryption/crypter.go index b1f8631ae26..7e69854c5a8 100644 --- a/pkg/encryption/crypter.go +++ b/pkg/encryption/crypter.go @@ -20,15 +20,15 @@ import ( "crypto/rand" "encoding/binary" "io" - "unsafe" "github.com/pingcap/kvproto/pkg/encryptionpb" "github.com/tikv/pd/pkg/errs" ) const ( - ivLengthCTR = 16 - ivLengthGCM = 12 + ivLengthCTR = 16 + ivLengthGCM = 12 + keyIDBufSize = 8 ) // CheckEncryptionMethodSupported check whether the encryption method is currently supported. @@ -106,7 +106,7 @@ func NewDataKey( if err != nil { return } - keyIDBufSize := unsafe.Sizeof(uint64(0)) + keyIDBuf := make([]byte, keyIDBufSize) n, err := io.ReadFull(rand.Reader, keyIDBuf) if err != nil { @@ -114,7 +114,7 @@ func NewDataKey( "fail to generate data key id") return } - if n != int(keyIDBufSize) { + if n != keyIDBufSize { err = errs.ErrEncryptionNewDataKey.GenWithStack( "no enough random bytes to generate data key id, bytes %d", n) return diff --git a/pkg/gctuner/finalizer_test.go b/pkg/gctuner/finalizer_test.go index 64cb308d931..9231ca633e5 100644 --- a/pkg/gctuner/finalizer_test.go +++ b/pkg/gctuner/finalizer_test.go @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build enable_flaky_tests - package gctuner import ( "runtime" "sync/atomic" "testing" + "time" "github.com/stretchr/testify/require" ) @@ -29,7 +28,7 @@ type testState struct { } func TestFinalizer(t *testing.T) { - maxCount := int32(16) + maxCount := int32(8) state := &testState{} f := newFinalizer(func() { n := atomic.AddInt32(&state.count, 1) @@ -39,6 +38,7 @@ func TestFinalizer(t *testing.T) { }) for i := int32(1); i <= maxCount; i++ { runtime.GC() + time.Sleep(10 * time.Millisecond) require.Equal(t, i, atomic.LoadInt32(&state.count)) } require.Nil(t, f.ref) diff --git a/pkg/gctuner/memory_limit_tuner_test.go b/pkg/gctuner/memory_limit_tuner_test.go index f56a64a7326..5e5f84ccbac 100644 --- a/pkg/gctuner/memory_limit_tuner_test.go +++ b/pkg/gctuner/memory_limit_tuner_test.go @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build enable_flaky_tests - package gctuner import ( @@ -47,9 +45,9 @@ func (a *mockAllocator) freeAll() { } func TestGlobalMemoryTuner(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/gctuner/testMemoryLimitTuner", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/tikv/pd/pkg/gctuner/testMemoryLimitTuner", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/gctuner/testMemoryLimitTuner")) + require.NoError(t, failpoint.Disable("github.com/tikv/pd/pkg/gctuner/testMemoryLimitTuner")) }() // Close GOGCTuner gogcTuner := EnableGOGCTuner.Load() @@ -61,11 +59,19 @@ func TestGlobalMemoryTuner(t *testing.T) { GlobalMemoryLimitTuner.UpdateMemoryLimit() require.True(t, GlobalMemoryLimitTuner.isTuning.Load()) defer func() { - time.Sleep(1 * time.Second) // If test.count > 1, wait tuning finished. - require.True(t, GlobalMemoryLimitTuner.isTuning.Load()) - // skip unstable test - // require.False(t, GlobalMemoryLimitTuner.waitingReset.Load()) - require.Equal(t, GlobalMemoryLimitTuner.nextGCTriggeredByMemoryLimit.Load(), false) + // If test.count > 1, wait tuning finished. + require.Eventually(t, func() bool { + //nolint: all_revive + return GlobalMemoryLimitTuner.isTuning.Load() + }, 5*time.Second, 100*time.Millisecond) + require.Eventually(t, func() bool { + //nolint: all_revive + return !GlobalMemoryLimitTuner.waitingReset.Load() + }, 5*time.Second, 100*time.Millisecond) + require.Eventually(t, func() bool { + //nolint: all_revive + return !GlobalMemoryLimitTuner.nextGCTriggeredByMemoryLimit.Load() + }, 5*time.Second, 100*time.Millisecond) }() allocator := &mockAllocator{} @@ -77,44 +83,43 @@ func TestGlobalMemoryTuner(t *testing.T) { } checkNextGCEqualMemoryLimit := func() { runtime.ReadMemStats(r) - // skip unstable test - // nextGC := r.NextGC - // memoryLimit := GlobalMemoryLimitTuner.calcMemoryLimit(GlobalMemoryLimitTuner.GetPercentage()) + nextGC := r.NextGC + memoryLimit := GlobalMemoryLimitTuner.calcMemoryLimit(GlobalMemoryLimitTuner.GetPercentage()) // In golang source, nextGC = memoryLimit - three parts memory. - // require.True(t, nextGC < uint64(memoryLimit)) + require.Less(t, nextGC, uint64(memoryLimit)) } memory600mb := allocator.alloc(600 << 20) gcNum := getNowGCNum() memory210mb := allocator.alloc(210 << 20) - time.Sleep(100 * time.Millisecond) - // skip unstable test - // require.True(t, GlobalMemoryLimitTuner.waitingReset.Load()) - require.True(t, gcNum < getNowGCNum()) + require.Eventually(t, func() bool { + return GlobalMemoryLimitTuner.waitingReset.Load() && gcNum < getNowGCNum() + }, 5*time.Second, 100*time.Millisecond) // Test waiting for reset - time.Sleep(500 * time.Millisecond) - require.Equal(t, GlobalMemoryLimitTuner.calcMemoryLimit(fallbackPercentage), debug.SetMemoryLimit(-1)) + require.Eventually(t, func() bool { + return GlobalMemoryLimitTuner.calcMemoryLimit(fallbackPercentage) == debug.SetMemoryLimit(-1) + }, 5*time.Second, 100*time.Millisecond) gcNum = getNowGCNum() memory100mb := allocator.alloc(100 << 20) - time.Sleep(100 * time.Millisecond) - require.Equal(t, gcNum, getNowGCNum()) // No GC + require.Eventually(t, func() bool { + return gcNum == getNowGCNum() + }, 5*time.Second, 100*time.Millisecond) // No GC allocator.free(memory210mb) allocator.free(memory100mb) runtime.GC() // Trigger GC in 80% again - time.Sleep(500 * time.Millisecond) - // skip unstable test - // require.Equal(t, GlobalMemoryLimitTuner.calcMemoryLimit(GlobalMemoryLimitTuner.GetPercentage()), debug.SetMemoryLimit(-1)) + require.Eventually(t, func() bool { + return GlobalMemoryLimitTuner.calcMemoryLimit(GlobalMemoryLimitTuner.GetPercentage()) == debug.SetMemoryLimit(-1) + }, 5*time.Second, 100*time.Millisecond) time.Sleep(100 * time.Millisecond) - // skip unstable test - // gcNum = getNowGCNum() + gcNum = getNowGCNum() checkNextGCEqualMemoryLimit() memory210mb = allocator.alloc(210 << 20) - time.Sleep(100 * time.Millisecond) - // skip unstable test - // require.True(t, gcNum < getNowGCNum()) + require.Eventually(t, func() bool { + return gcNum < getNowGCNum() + }, 5*time.Second, 100*time.Millisecond) allocator.free(memory210mb) allocator.free(memory600mb) } diff --git a/pkg/gctuner/tuner.go b/pkg/gctuner/tuner.go index 172c6adf326..74932fe174b 100644 --- a/pkg/gctuner/tuner.go +++ b/pkg/gctuner/tuner.go @@ -148,6 +148,10 @@ func (t *tuner) getGCPercent() uint32 { // tuning check the memory inuse and tune GC percent dynamically. // Go runtime ensure that it will be called serially. func (t *tuner) tuning() { + if !EnableGOGCTuner.Load() { + return + } + inuse := readMemoryInuse() threshold := t.getThreshold() log.Debug("tuning", zap.Uint64("inuse", inuse), zap.Uint64("threshold", threshold), diff --git a/pkg/gctuner/tuner_calc_test.go b/pkg/gctuner/tuner_calc_test.go deleted file mode 100644 index 473f5bda67d..00000000000 --- a/pkg/gctuner/tuner_calc_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2023 TiKV Project Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package gctuner - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestCalcGCPercent(t *testing.T) { - const gb = 1024 * 1024 * 1024 - // use default value when invalid params - require.Equal(t, defaultGCPercent, calcGCPercent(0, 0)) - require.Equal(t, defaultGCPercent, calcGCPercent(0, 1)) - require.Equal(t, defaultGCPercent, calcGCPercent(1, 0)) - - require.Equal(t, maxGCPercent.Load(), calcGCPercent(1, 3*gb)) - require.Equal(t, maxGCPercent.Load(), calcGCPercent(gb/10, 4*gb)) - require.Equal(t, maxGCPercent.Load(), calcGCPercent(gb/2, 4*gb)) - require.Equal(t, uint32(300), calcGCPercent(1*gb, 4*gb)) - require.Equal(t, uint32(166), calcGCPercent(1.5*gb, 4*gb)) - require.Equal(t, uint32(100), calcGCPercent(2*gb, 4*gb)) - require.Equal(t, uint32(100), calcGCPercent(3*gb, 4*gb)) - require.Equal(t, minGCPercent.Load(), calcGCPercent(4*gb, 4*gb)) - require.Equal(t, minGCPercent.Load(), calcGCPercent(5*gb, 4*gb)) -} diff --git a/pkg/gctuner/tuner_test.go b/pkg/gctuner/tuner_test.go index 604cd449b35..7018634c5d1 100644 --- a/pkg/gctuner/tuner_test.go +++ b/pkg/gctuner/tuner_test.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build enable_flaky_tests - package gctuner import ( "runtime" "testing" + "time" + "github.com/docker/go-units" "github.com/stretchr/testify/require" ) @@ -27,7 +27,7 @@ var testHeap []byte func TestTuner(t *testing.T) { EnableGOGCTuner.Store(true) - memLimit := uint64(1000 * 1024 * 1024) // 1000 MB + memLimit := uint64(1000 * units.MiB) // 1000 MB threshold := memLimit / 2 tn := newTuner(threshold) require.Equal(t, threshold, tn.threshold.Load()) @@ -39,7 +39,8 @@ func TestTuner(t *testing.T) { runtime.GC() for i := 0; i < 100; i++ { runtime.GC() - require.Equal(t, maxGCPercent.Load(), tn.getGCPercent()) + require.Eventually(t, func() bool { return maxGCPercent.Load() == tn.getGCPercent() }, + 1*time.Second, 50*time.Microsecond) } // 1/4 threshold @@ -55,8 +56,10 @@ func TestTuner(t *testing.T) { runtime.GC() for i := 0; i < 100; i++ { runtime.GC() - require.GreaterOrEqual(t, tn.getGCPercent(), minGCPercent.Load()) - require.LessOrEqual(t, tn.getGCPercent(), maxGCPercent.Load()/2) + require.Eventually(t, func() bool { return tn.getGCPercent() >= minGCPercent.Load() }, + 1*time.Second, 50*time.Microsecond) + require.Eventually(t, func() bool { return tn.getGCPercent() <= maxGCPercent.Load()/2 }, + 1*time.Second, 50*time.Microsecond) } // 3/4 threshold @@ -64,7 +67,8 @@ func TestTuner(t *testing.T) { runtime.GC() for i := 0; i < 100; i++ { runtime.GC() - require.Equal(t, minGCPercent.Load(), tn.getGCPercent()) + require.Eventually(t, func() bool { return minGCPercent.Load() == tn.getGCPercent() }, + 1*time.Second, 50*time.Microsecond) } // out of threshold @@ -72,6 +76,25 @@ func TestTuner(t *testing.T) { runtime.GC() for i := 0; i < 100; i++ { runtime.GC() - require.Equal(t, minGCPercent.Load(), tn.getGCPercent()) + require.Eventually(t, func() bool { return minGCPercent.Load() == tn.getGCPercent() }, + 1*time.Second, 50*time.Microsecond) } } + +func TestCalcGCPercent(t *testing.T) { + const gb = units.GiB + // use default value when invalid params + require.Equal(t, defaultGCPercent, calcGCPercent(0, 0)) + require.Equal(t, defaultGCPercent, calcGCPercent(0, 1)) + require.Equal(t, defaultGCPercent, calcGCPercent(1, 0)) + + require.Equal(t, maxGCPercent.Load(), calcGCPercent(1, 3*gb)) + require.Equal(t, maxGCPercent.Load(), calcGCPercent(gb/10, 4*gb)) + require.Equal(t, maxGCPercent.Load(), calcGCPercent(gb/2, 4*gb)) + require.Equal(t, uint32(300), calcGCPercent(1*gb, 4*gb)) + require.Equal(t, uint32(166), calcGCPercent(1.5*gb, 4*gb)) + require.Equal(t, uint32(100), calcGCPercent(2*gb, 4*gb)) + require.Equal(t, uint32(100), calcGCPercent(3*gb, 4*gb)) + require.Equal(t, minGCPercent.Load(), calcGCPercent(4*gb, 4*gb)) + require.Equal(t, minGCPercent.Load(), calcGCPercent(5*gb, 4*gb)) +} diff --git a/pkg/gogc/gogc.go b/pkg/gogc/gogc.go index 110b596fb89..2d3039ca244 100644 --- a/pkg/gogc/gogc.go +++ b/pkg/gogc/gogc.go @@ -19,9 +19,6 @@ import ( "runtime/debug" "strconv" "sync/atomic" - - "github.com/pingcap/log" - "go.uber.org/zap" ) var gogcValue int64 @@ -39,9 +36,6 @@ func SetGOGC(val int) int { val = 100 } result := debug.SetGCPercent(val) - if result != val { - log.Info("debug.SetGCPercent", zap.Int("val", val), zap.Int("result", result)) - } atomic.StoreInt64(&gogcValue, int64(val)) return result } diff --git a/pkg/mcs/server/server.go b/pkg/mcs/server/server.go index 6aec799278c..d30d3337a46 100644 --- a/pkg/mcs/server/server.go +++ b/pkg/mcs/server/server.go @@ -95,8 +95,8 @@ func (bs *BaseServer) GetHTTPClient() *http.Client { return bs.httpClient } -// SetETCDClient sets the etcd client. -func (bs *BaseServer) SetETCDClient(etcdClient *clientv3.Client) { +// SetEtcdClient sets the etcd client. +func (bs *BaseServer) SetEtcdClient(etcdClient *clientv3.Client) { bs.etcdClient = etcdClient } diff --git a/pkg/mcs/utils/util.go b/pkg/mcs/utils/util.go index 844cf17fde4..55e6c54e9b9 100644 --- a/pkg/mcs/utils/util.go +++ b/pkg/mcs/utils/util.go @@ -107,7 +107,7 @@ type server interface { GetGRPCServer() *grpc.Server SetGRPCServer(*grpc.Server) SetHTTPServer(*http.Server) - SetETCDClient(*clientv3.Client) + SetEtcdClient(*clientv3.Client) SetHTTPClient(*http.Client) IsSecure() bool RegisterGRPCService(*grpc.Server) @@ -183,7 +183,7 @@ func InitClient(s server) error { if err != nil { return err } - s.SetETCDClient(etcdClient) + s.SetEtcdClient(etcdClient) s.SetHTTPClient(etcdutil.CreateHTTPClient(tlsConfig)) return nil } diff --git a/pkg/schedule/checker/checker_controller.go b/pkg/schedule/checker/checker_controller.go index 627408e6c43..4704996f7d9 100644 --- a/pkg/schedule/checker/checker_controller.go +++ b/pkg/schedule/checker/checker_controller.go @@ -17,6 +17,7 @@ package checker import ( "bytes" "context" + "strconv" "time" "github.com/pingcap/failpoint" @@ -35,12 +36,16 @@ import ( ) const ( + suspectRegionLimit = 1024 checkSuspectRangesInterval = 100 * time.Millisecond // DefaultPendingRegionCacheSize is the default length of waiting list. DefaultPendingRegionCacheSize = 100000 - // It takes about 1.3 minutes(1000000/128*10/60/1000) to iterate 1 million regions(with DefaultPatrolRegionInterval=10ms). - patrolScanRegionLimit = 128 - suspectRegionLimit = 1024 + // For 1,024,000 regions, patrolRegionScanLimit is 1000, which is max(MinPatrolRegionScanLimit, 1,024,000/patrolRegionPartition) + // In order to avoid the patrolRegionScanLimit to be too big or too small, it will be limited to [128,8192]. + // It takes about 10s to iterate 1,024,000 regions(with DefaultPatrolRegionInterval=10ms) where other steps are not considered. + MinPatrolRegionScanLimit = 128 + MaxPatrolScanRegionLimit = 8192 + patrolRegionPartition = 1024 ) var ( @@ -76,6 +81,9 @@ type Controller struct { // It's used to update the ticker, so we need to // record it to avoid updating the ticker frequently. interval time.Duration + // patrolRegionScanLimit is the limit of regions to scan. + // It is calculated by the number of regions. + patrolRegionScanLimit int } // NewController create a new Controller. @@ -96,6 +104,7 @@ func NewController(ctx context.Context, cluster sche.CheckerCluster, conf config pendingProcessedRegions: pendingProcessedRegions, suspectKeyRanges: cache.NewStringTTL(ctx, time.Minute, 3*time.Minute), interval: cluster.GetCheckerConfig().GetPatrolRegionInterval(), + patrolRegionScanLimit: calculateScanLimit(cluster), } } @@ -113,36 +122,40 @@ func (c *Controller) PatrolRegions() { select { case <-ticker.C: c.updateTickerIfNeeded(ticker) + if c.cluster.IsSchedulingHalted() { + log.Debug("skip patrol regions due to scheduling is halted") + continue + } + + // Check priority regions first. + c.checkPriorityRegions() + // Check pending processed regions first. + c.checkPendingProcessedRegions() + + key, regions = c.checkRegions(key) + if len(regions) == 0 { + continue + } + // Updates the label level isolation statistics. + c.cluster.UpdateRegionsLabelLevelStats(regions) + // When the key is nil, it means that the scan is finished. + if len(key) == 0 { + // update the scan limit. + c.patrolRegionScanLimit = calculateScanLimit(c.cluster) + // update the metrics. + dur := time.Since(start) + patrolCheckRegionsGauge.Set(dur.Seconds()) + c.setPatrolRegionsDuration(dur) + start = time.Now() + } + failpoint.Inject("breakPatrol", func() { + failpoint.Return() + }) case <-c.ctx.Done(): patrolCheckRegionsGauge.Set(0) c.setPatrolRegionsDuration(0) return } - if c.cluster.IsSchedulingHalted() { - continue - } - - // Check priority regions first. - c.checkPriorityRegions() - // Check pending processed regions first. - c.checkPendingProcessedRegions() - - key, regions = c.checkRegions(key) - if len(regions) == 0 { - continue - } - // Updates the label level isolation statistics. - c.cluster.UpdateRegionsLabelLevelStats(regions) - // When the key is nil, it means that the scan is finished. - if len(key) == 0 { - dur := time.Since(start) - patrolCheckRegionsGauge.Set(dur.Seconds()) - c.setPatrolRegionsDuration(dur) - start = time.Now() - } - failpoint.Inject("breakPatrol", func() { - failpoint.Break() - }) } } @@ -160,7 +173,7 @@ func (c *Controller) setPatrolRegionsDuration(dur time.Duration) { } func (c *Controller) checkRegions(startKey []byte) (key []byte, regions []*core.RegionInfo) { - regions = c.cluster.ScanRegions(startKey, nil, patrolScanRegionLimit) + regions = c.cluster.ScanRegions(startKey, nil, c.patrolRegionScanLimit) if len(regions) == 0 { // Resets the scan key. key = nil @@ -439,3 +452,19 @@ func (c *Controller) updateTickerIfNeeded(ticker *time.Ticker) { log.Info("checkers starts patrol regions with new interval", zap.Duration("interval", newInterval)) } } + +// GetPatrolRegionScanLimit returns the limit of regions to scan. +// It only used for test. +func (c *Controller) GetPatrolRegionScanLimit() int { + return c.patrolRegionScanLimit +} + +func calculateScanLimit(cluster sche.CheckerCluster) int { + regionCount := cluster.GetTotalRegionCount() + failpoint.Inject("regionCount", func(val failpoint.Value) { + c, _ := strconv.ParseInt(val.(string), 10, 64) + regionCount = int(c) + }) + scanlimit := max(MinPatrolRegionScanLimit, regionCount/patrolRegionPartition) + return min(scanlimit, MaxPatrolScanRegionLimit) +} diff --git a/pkg/storage/hot_region_storage.go b/pkg/storage/hot_region_storage.go index 50fa7455f44..d323b40d435 100644 --- a/pkg/storage/hot_region_storage.go +++ b/pkg/storage/hot_region_storage.go @@ -37,7 +37,6 @@ import ( "github.com/tikv/pd/pkg/storage/kv" "github.com/tikv/pd/pkg/utils/logutil" "github.com/tikv/pd/pkg/utils/syncutil" - "github.com/tikv/pd/pkg/utils/typeutil" "go.uber.org/zap" ) @@ -267,8 +266,8 @@ func (h *HotRegionStorage) packHistoryHotRegions(historyHotRegions []HistoryHotR if err != nil { return err } - historyHotRegions[i].StartKey = typeutil.BytesToString(region.StartKey) - historyHotRegions[i].EndKey = typeutil.BytesToString(region.EndKey) + historyHotRegions[i].StartKey = string(region.StartKey) + historyHotRegions[i].EndKey = string(region.EndKey) key := HotRegionStorePath(hotRegionType, historyHotRegions[i].UpdateTime, historyHotRegions[i].RegionID) h.batchHotInfo[key] = &historyHotRegions[i] } @@ -386,8 +385,8 @@ func (it *HotRegionStorageIterator) Next() (*HistoryHotRegion, error) { if err := encryption.DecryptRegion(region, it.encryptionKeyManager); err != nil { return nil, err } - message.StartKey = typeutil.BytesToString(region.StartKey) - message.EndKey = typeutil.BytesToString(region.EndKey) + message.StartKey = string(region.StartKey) + message.EndKey = string(region.EndKey) message.EncryptionMeta = nil return &message, nil } diff --git a/pkg/utils/logutil/log.go b/pkg/utils/logutil/log.go index c7a9ac2f3b7..4854fd7ac40 100644 --- a/pkg/utils/logutil/log.go +++ b/pkg/utils/logutil/log.go @@ -23,7 +23,6 @@ import ( "github.com/pingcap/log" "github.com/tikv/pd/pkg/errs" - "github.com/tikv/pd/pkg/utils/typeutil" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -223,7 +222,7 @@ func RedactBytes(arg []byte) []byte { return []byte("?") case RedactInfoLogMarker: // Use unsafe conversion to avoid copy. - return typeutil.StringToBytes(redactInfo(typeutil.BytesToString(arg))) + return []byte(redactInfo(string(arg))) default: } return arg diff --git a/pkg/utils/typeutil/conversion.go b/pkg/utils/typeutil/conversion.go index dab12a52d9e..128c7a887a4 100644 --- a/pkg/utils/typeutil/conversion.go +++ b/pkg/utils/typeutil/conversion.go @@ -16,7 +16,6 @@ package typeutil import ( "encoding/binary" - "unsafe" "github.com/tikv/pd/pkg/errs" ) @@ -69,19 +68,3 @@ func JSONToUint64Slice(from any) ([]uint64, bool) { } return to, true } - -// BytesToString converts slice of bytes to string without copy. -func BytesToString(b []byte) string { - if len(b) == 0 { - return "" - } - return unsafe.String(unsafe.SliceData(b), len(b)) -} - -// StringToBytes converts string to slice of bytes without copy. -func StringToBytes(s string) []byte { - if len(s) == 0 { - return nil - } - return unsafe.Slice(unsafe.StringData(s), len(s)) -} diff --git a/pkg/utils/typeutil/conversion_test.go b/pkg/utils/typeutil/conversion_test.go index e69eeb57e23..7b17cfcbe2c 100644 --- a/pkg/utils/typeutil/conversion_test.go +++ b/pkg/utils/typeutil/conversion_test.go @@ -73,17 +73,3 @@ func TestJSONToUint64Slice(t *testing.T) { re.False(ok) re.Nil(res) } - -func TestBytesToString(t *testing.T) { - re := require.New(t) - str := "hello" - b := []byte(str) - re.Equal(str, BytesToString(b)) -} - -func TestStringToBytes(t *testing.T) { - re := require.New(t) - str := "hello" - b := StringToBytes(str) - re.Equal([]byte(str), b) -} diff --git a/server/api/label_test.go b/server/api/label_test.go index a8599273d5c..b8191a83753 100644 --- a/server/api/label_test.go +++ b/server/api/label_test.go @@ -22,6 +22,7 @@ import ( "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/kvproto/pkg/pdpb" "github.com/stretchr/testify/suite" + "github.com/tikv/pd/pkg/core" "github.com/tikv/pd/pkg/response" tu "github.com/tikv/pd/pkg/utils/testutil" "github.com/tikv/pd/server" @@ -277,6 +278,30 @@ func (suite *strictlyLabelsStoreTestSuite) TestStoreMatch() { valid: false, expectError: "key matching the label was not found", }, + { + store: &metapb.Store{ + Id: 3, + Address: "tiflash1", + State: metapb.StoreState_Up, + Labels: []*metapb.StoreLabel{ + { + Key: "zone", + Value: "us-west-1", + }, + { + Key: "disk", + Value: "ssd", + }, + { + Key: core.EngineKey, + Value: core.EngineTiFlash, + }, + }, + Version: "3.0.0", + }, + valid: true, + expectError: "placement rules is disabled", + }, } for _, testCase := range testCases { @@ -284,12 +309,16 @@ func (suite *strictlyLabelsStoreTestSuite) TestStoreMatch() { Header: &pdpb.RequestHeader{ClusterId: suite.svr.ClusterID()}, Store: &metapb.Store{ Id: testCase.store.Id, - Address: fmt.Sprintf("tikv%d", testCase.store.Id), + Address: testCase.store.Address, State: testCase.store.State, Labels: testCase.store.Labels, Version: testCase.store.Version, }, }) + if testCase.store.Address == "tiflash1" { + re.Contains(resp.GetHeader().GetError().String(), testCase.expectError) + continue + } if testCase.valid { re.NoError(err) re.Nil(resp.GetHeader().GetError()) @@ -309,7 +338,7 @@ func (suite *strictlyLabelsStoreTestSuite) TestStoreMatch() { Header: &pdpb.RequestHeader{ClusterId: suite.svr.ClusterID()}, Store: &metapb.Store{ Id: testCase.store.Id, - Address: fmt.Sprintf("tikv%d", testCase.store.Id), + Address: testCase.store.Address, State: testCase.store.State, Labels: testCase.store.Labels, Version: testCase.store.Version, diff --git a/server/cluster/cluster.go b/server/cluster/cluster.go index 0638c3903b9..c8013c63e2d 100644 --- a/server/cluster/cluster.go +++ b/server/cluster/cluster.go @@ -1307,6 +1307,9 @@ func (c *RaftCluster) checkStoreLabels(s *core.StoreInfo) error { } for _, label := range s.GetLabels() { key := label.GetKey() + if key == core.EngineKey { + continue + } if _, ok := keysSet[key]; !ok { log.Warn("not found the key match with the store label", zap.Stringer("store", s.GetMeta()), diff --git a/server/cluster/cluster_test.go b/server/cluster/cluster_test.go index c230339f079..3f01305b3f1 100644 --- a/server/cluster/cluster_test.go +++ b/server/cluster/cluster_test.go @@ -44,6 +44,7 @@ import ( "github.com/tikv/pd/pkg/mock/mockid" "github.com/tikv/pd/pkg/progress" "github.com/tikv/pd/pkg/schedule" + "github.com/tikv/pd/pkg/schedule/checker" sc "github.com/tikv/pd/pkg/schedule/config" sche "github.com/tikv/pd/pkg/schedule/core" "github.com/tikv/pd/pkg/schedule/filter" @@ -1850,7 +1851,7 @@ func Test(t *testing.T) { for i := uint64(0); i < n; i++ { region := regions[i] - regionKey := []byte{byte(i)} + regionKey := []byte(fmt.Sprintf("a%20d", i+1)) re.Nil(cache.GetRegion(i)) re.Nil(cache.GetRegionByKey(regionKey)) @@ -2184,7 +2185,7 @@ func newTestRegions(n, m, np uint64) []*core.RegionInfo { peers := make([]*metapb.Peer, 0, np) for j := uint64(0); j < np; j++ { peer := &metapb.Peer{ - Id: i*np + j, + Id: 100000000 + i*np + j, } peer.StoreId = (i + j) % m peers = append(peers, peer) @@ -2192,8 +2193,8 @@ func newTestRegions(n, m, np uint64) []*core.RegionInfo { region := &metapb.Region{ Id: i, Peers: peers, - StartKey: []byte{byte(i)}, - EndKey: []byte{byte(i + 1)}, + StartKey: []byte(fmt.Sprintf("a%20d", i+1)), + EndKey: []byte(fmt.Sprintf("a%20d", i+2)), RegionEpoch: &metapb.RegionEpoch{ConfVer: 2, Version: 2}, } regions = append(regions, core.NewRegionInfo(region, peers[0], core.SetApproximateSize(100), core.SetApproximateKeys(1000))) @@ -2881,6 +2882,55 @@ func TestCheckCache(t *testing.T) { re.NoError(failpoint.Disable("github.com/tikv/pd/pkg/schedule/checker/breakPatrol")) } +func TestScanLimit(t *testing.T) { + re := require.New(t) + + checkScanLimit(re, 1000, checker.MinPatrolRegionScanLimit) + checkScanLimit(re, 10000) + checkScanLimit(re, 100000) + checkScanLimit(re, 1000000) + checkScanLimit(re, 10000000, checker.MaxPatrolScanRegionLimit) +} + +func checkScanLimit(re *require.Assertions, regionCount int, expectScanLimit ...int) { + tc, co, cleanup := prepare(nil, nil, nil, re) + defer cleanup() + re.NoError(failpoint.Enable("github.com/tikv/pd/pkg/schedule/checker/breakPatrol", `return`)) + re.NoError(failpoint.Enable("github.com/tikv/pd/pkg/schedule/checker/regionCount", fmt.Sprintf("return(\"%d\")", regionCount))) + defer func() { + re.NoError(failpoint.Disable("github.com/tikv/pd/pkg/schedule/checker/breakPatrol")) + re.NoError(failpoint.Disable("github.com/tikv/pd/pkg/schedule/checker/regionCount")) + }() + + re.NoError(tc.addRegionStore(1, 0)) + re.NoError(tc.addRegionStore(2, 0)) + re.NoError(tc.addRegionStore(3, 0)) + regions := newTestRegions(10, 3, 3) + for i, region := range regions { + if i == 0 { + region.GetMeta().StartKey = []byte("") + } + if i == len(regions)-1 { + region.GetMeta().EndKey = []byte("") + } + re.NoError(tc.putRegion(region)) + } + + co.GetWaitGroup().Add(1) + co.PatrolRegions() + defer func() { + co.GetSchedulersController().Wait() + co.GetWaitGroup().Wait() + }() + + limit := co.GetCheckerController().GetPatrolRegionScanLimit() + re.LessOrEqual(checker.MinPatrolRegionScanLimit, limit) + re.GreaterOrEqual(checker.MaxPatrolScanRegionLimit, limit) + if len(expectScanLimit) > 0 { + re.Equal(expectScanLimit[0], limit) + } +} + func TestPeerState(t *testing.T) { re := require.New(t) diff --git a/server/gc_service.go b/server/gc_service.go index db3879a74fb..8c967d542ee 100644 --- a/server/gc_service.go +++ b/server/gc_service.go @@ -205,7 +205,7 @@ func (s *GrpcServer) GetAllGCSafePointV2(ctx context.Context, request *pdpb.GetA startkey := endpoint.GCSafePointV2Prefix() endkey := clientv3.GetPrefixRangeEnd(startkey) - _, values, revision, err := s.loadRangeFromETCD(startkey, endkey) + _, values, revision, err := s.loadRangeFromEtcd(startkey, endkey) gcSafePoints := make([]*pdpb.GCSafePointV2, 0, len(values)) for _, value := range values { @@ -236,7 +236,7 @@ func (s *GrpcServer) GetAllGCSafePointV2(ctx context.Context, request *pdpb.GetA }, nil } -func (s *GrpcServer) loadRangeFromETCD(startKey, endKey string) ([]string, []string, int64, error) { +func (s *GrpcServer) loadRangeFromEtcd(startKey, endKey string) ([]string, []string, int64, error) { startKey = strings.Join([]string{s.rootPath, startKey}, "/") var opOption []clientv3.OpOption if endKey == "\x00" { diff --git a/tests/integrations/client/http_client_test.go b/tests/integrations/client/http_client_test.go index 7b51d9917ad..fd8b65f01ba 100644 --- a/tests/integrations/client/http_client_test.go +++ b/tests/integrations/client/http_client_test.go @@ -440,6 +440,10 @@ func (suite *httpClientTestSuite) TestRegionLabel() { re.Equal(labelRule.ID, allLabelRules[1].ID) re.Equal(labelRule.Labels, allLabelRules[1].Labels) re.Equal(labelRule.RuleType, allLabelRules[1].RuleType) + labelRules, err = client.GetRegionLabelRulesByIDs(ctx, []string{"rule2"}) + re.NoError(err) + re.Len(labelRules, 1) + re.Equal(labelRule, labelRules[0]) labelRules, err = client.GetRegionLabelRulesByIDs(ctx, []string{"keyspaces/0", "rule2"}) re.NoError(err) sort.Slice(labelRules, func(i, j int) bool { diff --git a/tests/server/cluster/cluster_test.go b/tests/server/cluster/cluster_test.go index 5af750a3c2c..c95aa50cb3d 100644 --- a/tests/server/cluster/cluster_test.go +++ b/tests/server/cluster/cluster_test.go @@ -18,7 +18,9 @@ import ( "context" "fmt" "math" + "os" "strconv" + "strings" "sync" "testing" "time" @@ -1819,3 +1821,55 @@ func TestExternalTimestamp(t *testing.T) { re.Equal(ts, resp4.GetTimestamp()) } } + +func TestPatrolRegionConfigChange(t *testing.T) { + re := require.New(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tc, err := tests.NewTestCluster(ctx, 1) + defer tc.Destroy() + re.NoError(err) + err = tc.RunInitialServers() + re.NoError(err) + tc.WaitLeader() + leaderServer := tc.GetLeaderServer() + re.NoError(leaderServer.BootstrapCluster()) + for i := 1; i <= 3; i++ { + store := &metapb.Store{ + Id: uint64(i), + State: metapb.StoreState_Up, + NodeState: metapb.NodeState_Serving, + LastHeartbeat: time.Now().UnixNano(), + } + tests.MustPutStore(re, tc, store) + } + for i := 1; i <= 200; i++ { + startKey := []byte(fmt.Sprintf("%d", i*2-1)) + endKey := []byte(fmt.Sprintf("%d", i*2)) + tests.MustPutRegion(re, tc, uint64(i), uint64(i%3+1), startKey, endKey) + } + fname := testutil.InitTempFileLogger("debug") + defer os.RemoveAll(fname) + checkLog(re, fname, "coordinator starts patrol regions") + + // test change patrol region interval + schedule := leaderServer.GetConfig().Schedule + schedule.PatrolRegionInterval = typeutil.NewDuration(99 * time.Millisecond) + leaderServer.GetServer().SetScheduleConfig(schedule) + checkLog(re, fname, "starts patrol regions with new interval") + + // test change schedule halt + schedule = leaderServer.GetConfig().Schedule + schedule.HaltScheduling = true + leaderServer.GetServer().SetScheduleConfig(schedule) + checkLog(re, fname, "skip patrol regions due to scheduling is halted") +} + +func checkLog(re *require.Assertions, fname, expect string) { + testutil.Eventually(re, func() bool { + b, _ := os.ReadFile(fname) + l := string(b) + return strings.Contains(l, expect) + }) + os.Truncate(fname, 0) +} diff --git a/tools/pd-api-bench/cases/cases.go b/tools/pd-api-bench/cases/cases.go index 18d5c8732e6..6ffa429fdb7 100644 --- a/tools/pd-api-bench/cases/cases.go +++ b/tools/pd-api-bench/cases/cases.go @@ -117,18 +117,18 @@ func (c *baseCase) GetConfig() *Config { return c.cfg.Clone() } -// ETCDCase is the interface for all etcd api cases. -type ETCDCase interface { +// EtcdCase is the interface for all etcd api cases. +type EtcdCase interface { Case Init(context.Context, *clientv3.Client) error Unary(context.Context, *clientv3.Client) error } -// ETCDCreateFn is function type to create ETCDCase. -type ETCDCreateFn func() ETCDCase +// EtcdCreateFn is function type to create EtcdCase. +type EtcdCreateFn func() EtcdCase -// ETCDCaseFnMap is the map for all ETCD case creation function. -var ETCDCaseFnMap = map[string]ETCDCreateFn{ +// EtcdCaseFnMap is the map for all etcd case creation function. +var EtcdCaseFnMap = map[string]EtcdCreateFn{ "Get": newGetKV(), "Put": newPutKV(), "Delete": newDeleteKV(), @@ -440,8 +440,8 @@ type getKV struct { *baseCase } -func newGetKV() func() ETCDCase { - return func() ETCDCase { +func newGetKV() func() EtcdCase { + return func() EtcdCase { return &getKV{ baseCase: &baseCase{ name: "Get", @@ -470,8 +470,8 @@ type putKV struct { *baseCase } -func newPutKV() func() ETCDCase { - return func() ETCDCase { +func newPutKV() func() EtcdCase { + return func() EtcdCase { return &putKV{ baseCase: &baseCase{ name: "Put", @@ -492,8 +492,8 @@ type deleteKV struct { *baseCase } -func newDeleteKV() func() ETCDCase { - return func() ETCDCase { +func newDeleteKV() func() EtcdCase { + return func() EtcdCase { return &deleteKV{ baseCase: &baseCase{ name: "Put", @@ -514,8 +514,8 @@ type txnKV struct { *baseCase } -func newTxnKV() func() ETCDCase { - return func() ETCDCase { +func newTxnKV() func() EtcdCase { + return func() EtcdCase { return &txnKV{ baseCase: &baseCase{ name: "Put", diff --git a/tools/pd-api-bench/cases/controller.go b/tools/pd-api-bench/cases/controller.go index 42eeafe4597..dc48b5280cf 100644 --- a/tools/pd-api-bench/cases/controller.go +++ b/tools/pd-api-bench/cases/controller.go @@ -77,8 +77,8 @@ func (c *Coordinator) GetGRPCCase(name string) (*Config, error) { return nil, errors.Errorf("case %v does not exist", name) } -// GetETCDCase returns the etcd case config. -func (c *Coordinator) GetETCDCase(name string) (*Config, error) { +// GetEtcdCase returns the etcd case config. +func (c *Coordinator) GetEtcdCase(name string) (*Config, error) { c.mu.RLock() defer c.mu.RUnlock() if controller, ok := c.etcd[name]; ok { @@ -109,8 +109,8 @@ func (c *Coordinator) GetAllGRPCCases() map[string]*Config { return ret } -// GetAllETCDCases returns the all etcd case configs. -func (c *Coordinator) GetAllETCDCases() map[string]*Config { +// GetAllEtcdCases returns the all etcd case configs. +func (c *Coordinator) GetAllEtcdCases() map[string]*Config { c.mu.RLock() defer c.mu.RUnlock() ret := make(map[string]*Config) @@ -164,11 +164,11 @@ func (c *Coordinator) SetGRPCCase(name string, cfg *Config) error { return nil } -// SetETCDCase sets the config for the specific case. -func (c *Coordinator) SetETCDCase(name string, cfg *Config) error { +// SetEtcdCase sets the config for the specific case. +func (c *Coordinator) SetEtcdCase(name string, cfg *Config) error { c.mu.Lock() defer c.mu.Unlock() - if fn, ok := ETCDCaseFnMap[name]; ok { + if fn, ok := EtcdCaseFnMap[name]; ok { var controller *etcdController if controller, ok = c.etcd[name]; !ok { controller = newEtcdController(c.ctx, c.etcdClients, fn) @@ -324,7 +324,7 @@ func (c *gRPCController) stop() { } type etcdController struct { - ETCDCase + EtcdCase clients []*clientv3.Client pctx context.Context @@ -334,11 +334,11 @@ type etcdController struct { wg sync.WaitGroup } -func newEtcdController(ctx context.Context, clis []*clientv3.Client, fn ETCDCreateFn) *etcdController { +func newEtcdController(ctx context.Context, clis []*clientv3.Client, fn EtcdCreateFn) *etcdController { c := &etcdController{ pctx: ctx, clients: clis, - ETCDCase: fn(), + EtcdCase: fn(), } return c } diff --git a/tools/pd-api-bench/config/config.go b/tools/pd-api-bench/config/config.go index 35377c12f33..0320665c29b 100644 --- a/tools/pd-api-bench/config/config.go +++ b/tools/pd-api-bench/config/config.go @@ -44,7 +44,7 @@ type Config struct { // only for init HTTP map[string]cases.Config `toml:"http" json:"http"` GRPC map[string]cases.Config `toml:"grpc" json:"grpc"` - ETCD map[string]cases.Config `toml:"etcd" json:"etcd"` + Etcd map[string]cases.Config `toml:"etcd" json:"etcd"` } // NewConfig return a set of settings. @@ -109,9 +109,9 @@ func (c *Config) InitCoordinator(co *cases.Coordinator) { log.Error("create gRPC case failed", zap.Error(err)) } } - for name, cfg := range c.ETCD { + for name, cfg := range c.Etcd { cfg := cfg - err := co.SetETCDCase(name, &cfg) + err := co.SetEtcdCase(name, &cfg) if err != nil { log.Error("create etcd case failed", zap.Error(err)) } diff --git a/tools/pd-api-bench/main.go b/tools/pd-api-bench/main.go index 747fbaa10c1..78bec1e1d01 100644 --- a/tools/pd-api-bench/main.go +++ b/tools/pd-api-bench/main.go @@ -292,14 +292,14 @@ func runHTTPServer(cfg *config.Config, co *cases.Coordinator) { } for name, cfg := range input { cfg := cfg - co.SetETCDCase(name, &cfg) + co.SetEtcdCase(name, &cfg) } c.String(http.StatusOK, "") }) engine.POST("config/etcd/:name", func(c *gin.Context) { name := c.Param("name") cfg := getCfg(c) - co.SetETCDCase(name, cfg) + co.SetEtcdCase(name, cfg) c.String(http.StatusOK, "") }) @@ -330,12 +330,12 @@ func runHTTPServer(cfg *config.Config, co *cases.Coordinator) { c.IndentedJSON(http.StatusOK, cfg) }) engine.GET("config/etcd/all", func(c *gin.Context) { - all := co.GetAllETCDCases() + all := co.GetAllEtcdCases() c.IndentedJSON(http.StatusOK, all) }) engine.GET("config/etcd/:name", func(c *gin.Context) { name := c.Param("name") - cfg, err := co.GetETCDCase(name) + cfg, err := co.GetEtcdCase(name) if err != nil { c.String(http.StatusBadRequest, err.Error()) return diff --git a/tools/pd-ut/ut.go b/tools/pd-ut/ut.go index e0312272310..6b366a137a2 100644 --- a/tools/pd-ut/ut.go +++ b/tools/pd-ut/ut.go @@ -92,7 +92,7 @@ go tool cover --func=xxx` } var ( - modulePath = "github.com/tikv/pd" + modulePath = filepath.Join("github.com", "tikv", "pd") integrationsTestPath = filepath.Join("tests", "integrations") ) @@ -414,7 +414,7 @@ func runExistingTestCases(pkgs []string) (tasks []task, err error) { wg := &sync.WaitGroup{} tasksChannel := make(chan []task, len(pkgs)) for _, pkg := range pkgs { - _, ok := existPkgs[fmt.Sprintf("%s/%s", modulePath, pkg)] + _, ok := existPkgs[filepath.Join(modulePath, pkg)] if !ok { fmt.Println("no test case in ", pkg) continue @@ -528,7 +528,8 @@ func filterTestCases(tasks []task, arg1 string) ([]task, error) { } func listPackages() ([]string, error) { - cmd := exec.Command("go", "list", "./...") + listPath := strings.Join([]string{".", "..."}, string(filepath.Separator)) + cmd := exec.Command("go", "list", listPath) cmd.Dir = workDir ss, err := cmdToLines(cmd) if err != nil { @@ -677,7 +678,7 @@ func (*numa) testCommand(pkg string, fn string) *exec.Cmd { args = append(args, "-test.v") exe := strings.Join([]string{".", testFileName(pkg)}, string(filepath.Separator)) if coverProfile != "" { - fileName := strings.ReplaceAll(pkg, "/", "_") + "." + fn + fileName := strings.ReplaceAll(pkg, string(filepath.Separator), "_") + "." + fn tmpFile := filepath.Join(coverFileTempDir, fileName) args = append(args, "-test.coverprofile", tmpFile) } @@ -720,12 +721,12 @@ func generateBuildCache() error { fmt.Println("generate build cache") // cd cmd/pd-server && go test -tags=tso_function_test,deadlock -exec-=true -vet=off -toolexec=go-compile-without-link cmd := exec.Command("go", "test", "-exec=true", "-vet", "off", "--tags=tso_function_test,deadlock") - goCompileWithoutLink := fmt.Sprintf("-toolexec=%s/tools/pd-ut/go-compile-without-link.sh", workDir) - cmd.Dir = fmt.Sprintf("%s/cmd/pd-server", workDir) + goCompileWithoutLink := fmt.Sprintf("-toolexec=%s", filepath.Join(workDir, "tools", "pd-ut", "go-compile-without-link.sh")) + cmd.Dir = filepath.Join(workDir, "cmd", "pd-server") if strings.Contains(workDir, integrationsTestPath) { - cmd.Dir = fmt.Sprintf("%s/cmd/pd-server", workDir[:strings.LastIndex(workDir, integrationsTestPath)]) - goCompileWithoutLink = fmt.Sprintf("-toolexec=%s/tools/pd-ut/go-compile-without-link.sh", - workDir[:strings.LastIndex(workDir, integrationsTestPath)]) + cmd.Dir = filepath.Join(workDir[:strings.LastIndex(workDir, integrationsTestPath)], "cmd", "pd-server") + goCompileWithoutLink = fmt.Sprintf("-toolexec=%s", filepath.Join(workDir[:strings.LastIndex(workDir, integrationsTestPath)], + "tools", "pd-ut", "go-compile-without-link.sh")) } cmd.Args = append(cmd.Args, goCompileWithoutLink) cmd.Stdout = os.Stdout @@ -759,11 +760,11 @@ func buildTestBinaryMulti(pkgs []string) ([]byte, error) { p := strconv.Itoa(parallel * 2) cmd := exec.Command("go", "test", "-p", p, "--exec", xprogPath, "-vet", "off", "--tags=tso_function_test,deadlock") if coverProfile != "" { - coverpkg := "./..." + coverPkg := strings.Join([]string{".", "..."}, string(filepath.Separator)) if strings.Contains(workDir, integrationsTestPath) { - coverpkg = "../../..." + coverPkg = filepath.Join("..", "..", "...") } - cmd.Args = append(cmd.Args, "-cover", fmt.Sprintf("-coverpkg=%s", coverpkg)) + cmd.Args = append(cmd.Args, "-cover", fmt.Sprintf("-coverpkg=%s", coverPkg)) } cmd.Args = append(cmd.Args, packages...) if race { @@ -794,7 +795,8 @@ func buildTestBinary(pkg string) error { //nolint:gosec cmd := exec.Command("go", "test", "-c", "-vet", "off", "--tags=tso_function_test,deadlock", "-o", testFileName(pkg), "-v") if coverProfile != "" { - cmd.Args = append(cmd.Args, "-cover", "-coverpkg=./...") + coverPkg := strings.Join([]string{".", "..."}, string(filepath.Separator)) + cmd.Args = append(cmd.Args, "-cover", fmt.Sprintf("-coverpkg=%s", coverPkg)) } if race { cmd.Args = append(cmd.Args, "-race") diff --git a/tools/pd-ut/xprog.go b/tools/pd-ut/xprog.go index cf3e9b295e2..4a593be8f31 100644 --- a/tools/pd-ut/xprog.go +++ b/tools/pd-ut/xprog.go @@ -34,7 +34,7 @@ func main() { // Extract the current work directory cwd := os.Args[0] - cwd = cwd[:len(cwd)-len("bin/xprog")] + cwd = cwd[:len(cwd)-len(filepath.Join("bin", "xprog"))] testBinaryPath := os.Args[1] dir, _ := filepath.Split(testBinaryPath) @@ -42,7 +42,7 @@ func main() { // Extract the package info from /tmp/go-build2662369829/b1382/importcfg.link pkg := getPackageInfo(dir) - const prefix = "github.com/tikv/pd/" + var prefix = filepath.Join("github.com", "tikv", "pd") if !strings.HasPrefix(pkg, prefix) { os.Exit(-3) }