diff --git a/pkg/mock/mockcluster/mockcluster.go b/pkg/mock/mockcluster/mockcluster.go index ea91168ea8e..4f073ef9df1 100644 --- a/pkg/mock/mockcluster/mockcluster.go +++ b/pkg/mock/mockcluster/mockcluster.go @@ -595,6 +595,7 @@ func (mc *Cluster) RemoveScheduler(name string) error { } // MockRegionInfo returns a mock region +// If leaderStoreID is zero, the regions would have no leader func (mc *Cluster) MockRegionInfo(regionID uint64, leaderStoreID uint64, followerStoreIDs, learnerStoreIDs []uint64, epoch *metapb.RegionEpoch) *core.RegionInfo { @@ -604,8 +605,11 @@ func (mc *Cluster) MockRegionInfo(regionID uint64, leaderStoreID uint64, EndKey: []byte(fmt.Sprintf("%20d", regionID+1)), RegionEpoch: epoch, } - leader, _ := mc.AllocPeer(leaderStoreID) - region.Peers = []*metapb.Peer{leader} + var leader *metapb.Peer + if leaderStoreID != 0 { + leader, _ = mc.AllocPeer(leaderStoreID) + region.Peers = append(region.Peers, leader) + } for _, storeID := range followerStoreIDs { peer, _ := mc.AllocPeer(storeID) region.Peers = append(region.Peers, peer) diff --git a/server/schedule/filter/filters.go b/server/schedule/filter/filters.go index 962b698ca9f..0cea7c925fc 100644 --- a/server/schedule/filter/filters.go +++ b/server/schedule/filter/filters.go @@ -16,6 +16,7 @@ package filter import ( "fmt" + "github.com/golang/protobuf/proto" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/log" "github.com/tikv/pd/pkg/slice" @@ -722,11 +723,7 @@ var allSpeicalEngines = []string{EngineTiFlash} // FitRegion in filter func createRegionForRuleFit(startKey, endKey []byte, peers []*metapb.Peer, leader *metapb.Peer, opts ...core.RegionCreateOption) *core.RegionInfo { - copyLeader := &metapb.Peer{ - Id: leader.Id, - StoreId: leader.StoreId, - IsLearner: leader.IsLearner, - } + copyLeader := proto.Clone(leader).(*metapb.Peer) copyPeers := make([]*metapb.Peer, 0, len(peers)) for _, p := range peers { peer := &metapb.Peer{ diff --git a/server/schedulers/balance_region.go b/server/schedulers/balance_region.go index 77b21af247c..0a773c7b0f4 100644 --- a/server/schedulers/balance_region.go +++ b/server/schedulers/balance_region.go @@ -173,6 +173,12 @@ func (s *balanceRegionScheduler) Schedule(cluster opt.Cluster) []*operator.Opera schedulerCounter.WithLabelValues(s.GetName(), "region-hot").Inc() continue } + // Check region whether have leader + if region.GetLeader() == nil { + log.Warn("region have no leader", zap.String("scheduler", s.GetName()), zap.Uint64("region-id", region.GetID())) + schedulerCounter.WithLabelValues(s.GetName(), "no-leader").Inc() + continue + } oldPeer := region.GetStorePeer(sourceID) if op := s.transferPeer(cluster, region, oldPeer); op != nil { diff --git a/server/schedulers/balance_test.go b/server/schedulers/balance_test.go index 19be8e2625c..0aac3b10087 100644 --- a/server/schedulers/balance_test.go +++ b/server/schedulers/balance_test.go @@ -1125,6 +1125,22 @@ func (s *testReplicaCheckerSuite) TestOpts(c *C) { c.Assert(rc.Check(region), IsNil) } +func (s *testBalanceRegionSchedulerSuite) TestShouldNotBalance(c *C) { + opt := mockoption.NewScheduleOptions() + tc := mockcluster.NewCluster(opt) + oc := schedule.NewOperatorController(s.ctx, nil, nil) + sb, err := schedule.CreateScheduler(BalanceRegionType, oc, core.NewStorage(kv.NewMemoryKV()), schedule.ConfigSliceDecoder(BalanceRegionType, []string{"", ""})) + c.Assert(err, IsNil) + region := tc.MockRegionInfo(1, 0, []uint64{2, 3, 4}, nil, nil) + tc.PutRegion(region) + operators := sb.Schedule(tc) + if operators != nil { + c.Assert(len(operators), Equals, 0) + } else { + c.Assert(operators, IsNil) + } +} + var _ = Suite(&testRandomMergeSchedulerSuite{}) type testRandomMergeSchedulerSuite struct{}