@@ -20,6 +20,7 @@ import (
2020 "github.com/pingcap/kvproto/pkg/metapb"
2121 "github.com/pingcap/pd/server/core"
2222 "github.com/pingcap/pd/server/namespace"
23+ "github.com/pkg/errors"
2324)
2425
2526type selectedStores struct {
@@ -79,23 +80,28 @@ func NewRegionScatterer(cluster Cluster, classifier namespace.Classifier) *Regio
7980}
8081
8182// Scatter relocates the region.
82- func (r * RegionScatterer ) Scatter (region * core.RegionInfo ) * Operator {
83+ func (r * RegionScatterer ) Scatter (region * core.RegionInfo ) ( * Operator , error ) {
8384 if r .cluster .IsRegionHot (region .GetID ()) {
84- return nil
85+ return nil , errors . Errorf ( "region %d is a hot region" , region . GetID ())
8586 }
8687
8788 if len (region .GetPeers ()) != r .cluster .GetMaxReplicas () {
88- return nil
89+ return nil , errors .Errorf ("the number replicas of region %d is not expected" , region .GetID ())
90+ }
91+
92+ if region .GetLeader () == nil {
93+ return nil , errors .Errorf ("region %d has no leader" , region .GetID ())
8994 }
9095
91- return r .scatterRegion (region )
96+ return r .scatterRegion (region ), nil
9297}
9398
9499func (r * RegionScatterer ) scatterRegion (region * core.RegionInfo ) * Operator {
95- steps := make ([]OperatorStep , 0 , len (region .GetPeers ()))
96-
97100 stores := r .collectAvailableStores (region )
98- var kind OperatorKind
101+ var (
102+ targetPeers []* metapb.Peer
103+ replacedPeers []* metapb.Peer
104+ )
99105 for _ , peer := range region .GetPeers () {
100106 if len (stores ) == 0 {
101107 // Reset selected stores if we have no available stores.
@@ -105,31 +111,93 @@ func (r *RegionScatterer) scatterRegion(region *core.RegionInfo) *Operator {
105111
106112 if r .selected .put (peer .GetStoreId ()) {
107113 delete (stores , peer .GetStoreId ())
114+ targetPeers = append (targetPeers , peer )
115+ replacedPeers = append (replacedPeers , peer )
108116 continue
109117 }
110118 newPeer := r .selectPeerToReplace (stores , region , peer )
111119 if newPeer == nil {
120+ targetPeers = append (targetPeers , peer )
121+ replacedPeers = append (replacedPeers , peer )
112122 continue
113123 }
114-
115124 // Remove it from stores and mark it as selected.
116125 delete (stores , newPeer .GetStoreId ())
117126 r .selected .put (newPeer .GetStoreId ())
127+ targetPeers = append (targetPeers , newPeer )
128+ replacedPeers = append (replacedPeers , peer )
129+ }
130+ return r .createOperator (region , replacedPeers , targetPeers )
131+ }
118132
119- op , err := CreateMovePeerOperator ("scatter-peer" , r .cluster , region , OpAdmin ,
120- peer .GetStoreId (), newPeer .GetStoreId (), newPeer .GetId ())
121- if err != nil {
122- continue
133+ func (r * RegionScatterer ) createOperator (origin * core.RegionInfo , replacedPeers , targetPeers []* metapb.Peer ) * Operator {
134+ // Randomly pick a leader
135+ i := rand .Intn (len (targetPeers ))
136+ targetLeaderPeer := targetPeers [i ]
137+ originLeaderStoreID := origin .GetLeader ().GetStoreId ()
138+
139+ originStoreIDs := origin .GetStoreIds ()
140+ steps := make ([]OperatorStep , 0 , len (targetPeers )* 3 + 1 )
141+ // deferSteps will append to the end of the steps
142+ deferSteps := make ([]OperatorStep , 0 , 5 )
143+ var kind OperatorKind
144+ sameLeader := targetLeaderPeer .GetStoreId () == originLeaderStoreID
145+ // No need to do anything
146+ if sameLeader {
147+ isSame := true
148+ for _ , peer := range targetPeers {
149+ if _ , ok := originStoreIDs [peer .GetStoreId ()]; ! ok {
150+ isSame = false
151+ break
152+ }
153+ }
154+ if isSame {
155+ return nil
123156 }
124- steps = append (steps , op .steps ... )
125- steps = append (steps , TransferLeader {ToStore : newPeer .GetStoreId ()})
126- kind |= op .Kind ()
127157 }
128158
129- if len (steps ) == 0 {
130- return nil
159+ // Creates the first step
160+ if _ , ok := originStoreIDs [targetLeaderPeer .GetStoreId ()]; ! ok {
161+ st := CreateAddPeerSteps (targetLeaderPeer .GetStoreId (), targetLeaderPeer .GetId (), r .cluster )
162+ steps = append (steps , st ... )
163+ // Do not transfer leader to the newly added peer
164+ // Ref: https://github.com/tikv/tikv/issues/3819
165+ deferSteps = append (deferSteps , TransferLeader {FromStore : originLeaderStoreID , ToStore : targetLeaderPeer .GetStoreId ()})
166+ deferSteps = append (deferSteps , RemovePeer {FromStore : replacedPeers [i ].GetStoreId ()})
167+ kind |= OpLeader
168+ kind |= OpRegion
169+ } else {
170+ if ! sameLeader {
171+ steps = append (steps , TransferLeader {FromStore : originLeaderStoreID , ToStore : targetLeaderPeer .GetStoreId ()})
172+ kind |= OpLeader
173+ }
174+ }
175+
176+ // For the other steps
177+ for j , peer := range targetPeers {
178+ if peer .GetId () == targetLeaderPeer .GetId () {
179+ continue
180+ }
181+ if _ , ok := originStoreIDs [peer .GetStoreId ()]; ok {
182+ continue
183+ }
184+ if replacedPeers [j ].GetStoreId () == originLeaderStoreID {
185+ st := CreateAddPeerSteps (peer .GetStoreId (), peer .GetId (), r .cluster )
186+ st = append (st , RemovePeer {FromStore : replacedPeers [j ].GetStoreId ()})
187+ deferSteps = append (deferSteps , st ... )
188+ kind |= OpRegion | OpLeader
189+ continue
190+ }
191+ st := CreateAddPeerSteps (peer .GetStoreId (), peer .GetId (), r .cluster )
192+ steps = append (steps , st ... )
193+ steps = append (steps , RemovePeer {FromStore : replacedPeers [j ].GetStoreId ()})
194+ kind |= OpRegion
131195 }
132- return NewOperator ("scatter-region" , region .GetID (), region .GetRegionEpoch (), kind , steps ... )
196+
197+ steps = append (steps , deferSteps ... )
198+ op := NewOperator ("scatter-region" , origin .GetID (), origin .GetRegionEpoch (), kind , steps ... )
199+ op .SetPriorityLevel (core .HighPriority )
200+ return op
133201}
134202
135203func (r * RegionScatterer ) selectPeerToReplace (stores map [uint64 ]* core.StoreInfo , region * core.RegionInfo , oldPeer * metapb.Peer ) * metapb.Peer {
0 commit comments