@@ -18,6 +18,7 @@ import (
1818 "context"
1919 "fmt"
2020 "math"
21+ "strconv"
2122 "sync"
2223 "time"
2324
@@ -87,26 +88,6 @@ func (s *selectedStores) GetGroupDistribution(group string) (map[uint64]uint64,
8788 return s .getDistributionByGroupLocked (group )
8889}
8990
90- // TotalCountByStore counts the total count by store
91- func (s * selectedStores ) TotalCountByStore (storeID uint64 ) uint64 {
92- s .mu .RLock ()
93- defer s .mu .RUnlock ()
94- groups := s .groupDistribution .GetAllID ()
95- totalCount := uint64 (0 )
96- for _ , group := range groups {
97- storeDistribution , ok := s .getDistributionByGroupLocked (group )
98- if ! ok {
99- continue
100- }
101- count , ok := storeDistribution [storeID ]
102- if ! ok {
103- continue
104- }
105- totalCount += count
106- }
107- return totalCount
108- }
109-
11091// getDistributionByGroupLocked should be called with lock
11192func (s * selectedStores ) getDistributionByGroupLocked (group string ) (map [uint64 ]uint64 , bool ) {
11293 if result , ok := s .groupDistribution .Get (group ); ok {
@@ -315,6 +296,12 @@ func (r *RegionScatterer) scatterRegion(region *core.RegionInfo, group string) *
315296 selectedStores := make (map [uint64 ]struct {}, len (region .GetPeers ())) // selected StoreID set
316297 leaderCandidateStores := make ([]uint64 , 0 , len (region .GetPeers ())) // StoreID allowed to become Leader
317298 scatterWithSameEngine := func (peers map [uint64 ]* metapb.Peer , context engineContext ) { // peers: StoreID -> Peer
299+ filterLen := len (context .filterFuncs ) + 2
300+ filters := make ([]filter.Filter , filterLen )
301+ for i , filterFunc := range context .filterFuncs {
302+ filters [i ] = filterFunc ()
303+ }
304+ filters [filterLen - 2 ] = filter .NewExcludedFilter (r .name , nil , selectedStores )
318305 for _ , peer := range peers {
319306 if _ , ok := selectedStores [peer .GetStoreId ()]; ok {
320307 if allowLeader (oldFit , peer ) {
@@ -323,9 +310,14 @@ func (r *RegionScatterer) scatterRegion(region *core.RegionInfo, group string) *
323310 // It is both sourcePeer and targetPeer itself, no need to select.
324311 continue
325312 }
313+ sourceStore := r .cluster .GetStore (peer .GetStoreId ())
314+ if sourceStore == nil {
315+ log .Error ("failed to get the store" , zap .Uint64 ("store-id" , peer .GetStoreId ()), errs .ZapError (errs .ErrGetSourceStore ))
316+ continue
317+ }
318+ filters [filterLen - 1 ] = filter .NewPlacementSafeguard (r .name , r .cluster .GetOpts (), r .cluster .GetBasicCluster (), r .cluster .GetRuleManager (), region , sourceStore , oldFit )
326319 for {
327- candidates := r .selectCandidates (region , oldFit , peer .GetStoreId (), selectedStores , context )
328- newPeer := r .selectStore (group , peer , peer .GetStoreId (), candidates , context )
320+ newPeer := r .selectNewPeer (context , group , peer , filters )
329321 targetPeers [newPeer .GetStoreId ()] = newPeer
330322 selectedStores [newPeer .GetStoreId ()] = struct {}{}
331323 // If the selected peer is a peer other than origin peer in this region,
@@ -346,7 +338,7 @@ func (r *RegionScatterer) scatterRegion(region *core.RegionInfo, group string) *
346338 // FIXME: target leader only considers the ordinary stores, maybe we need to consider the
347339 // special engine stores if the engine supports to become a leader. But now there is only
348340 // one engine, tiflash, which does not support the leader, so don't consider it for now.
349- targetLeader := r .selectAvailableLeaderStore (group , region , leaderCandidateStores , r .ordinaryEngine )
341+ targetLeader , leaderStorePickedCount := r .selectAvailableLeaderStore (group , region , leaderCandidateStores , r .ordinaryEngine )
350342 if targetLeader == 0 {
351343 scatterCounter .WithLabelValues ("no-leader" , "" ).Inc ()
352344 return nil
@@ -381,6 +373,8 @@ func (r *RegionScatterer) scatterRegion(region *core.RegionInfo, group string) *
381373 if op != nil {
382374 scatterCounter .WithLabelValues ("success" , "" ).Inc ()
383375 r .Put (targetPeers , targetLeader , group )
376+ op .AdditionalInfos ["group" ] = group
377+ op .AdditionalInfos ["leader-picked-count" ] = strconv .FormatUint (leaderStorePickedCount , 10 )
384378 op .SetPriorityLevel (core .High )
385379 }
386380 return op
@@ -418,69 +412,52 @@ func isSameDistribution(region *core.RegionInfo, targetPeers map[uint64]*metapb.
418412 return region .GetLeader ().GetStoreId () == targetLeader
419413}
420414
421- func (r * RegionScatterer ) selectCandidates (region * core.RegionInfo , oldFit * placement.RegionFit , sourceStoreID uint64 , selectedStores map [uint64 ]struct {}, context engineContext ) []uint64 {
422- sourceStore := r .cluster .GetStore (sourceStoreID )
423- if sourceStore == nil {
424- log .Error ("failed to get the store" , zap .Uint64 ("store-id" , sourceStoreID ), errs .ZapError (errs .ErrGetSourceStore ))
425- return nil
426- }
427- filters := []filter.Filter {
428- filter .NewExcludedFilter (r .name , nil , selectedStores ),
429- }
430- scoreGuard := filter .NewPlacementSafeguard (r .name , r .cluster .GetOpts (), r .cluster .GetBasicCluster (), r .cluster .GetRuleManager (), region , sourceStore , oldFit )
431- for _ , filterFunc := range context .filterFuncs {
432- filters = append (filters , filterFunc ())
433- }
434- filters = append (filters , scoreGuard )
415+ // selectNewPeer return the new peer which pick the fewest picked count.
416+ // it keeps the origin peer if the origin store's pick count is equal the fewest pick.
417+ // it can be diveded into three steps:
418+ // 1. found the max pick count and the min pick count.
419+ // 2. if max pick count equals min pick count, it means all store picked count are some, return the origin peer.
420+ // 3. otherwise, select the store which pick count is the min pick count and pass all filter.
421+ func (r * RegionScatterer ) selectNewPeer (context engineContext , group string , peer * metapb.Peer , filters []filter.Filter ) * metapb.Peer {
435422 stores := r .cluster .GetStores ()
436- candidates := make ([]uint64 , 0 )
437423 maxStoreTotalCount := uint64 (0 )
438424 minStoreTotalCount := uint64 (math .MaxUint64 )
439425 for _ , store := range stores {
440- count := context .selectedPeer .TotalCountByStore (store .GetID ())
426+ count := context .selectedPeer .Get (store .GetID (), group )
441427 if count > maxStoreTotalCount {
442428 maxStoreTotalCount = count
443429 }
444430 if count < minStoreTotalCount {
445431 minStoreTotalCount = count
446432 }
447433 }
434+
435+ var newPeer * metapb.Peer
436+ minCount := uint64 (math .MaxUint64 )
437+ originStorePickedCount := uint64 (math .MaxUint64 )
448438 for _ , store := range stores {
449- storeCount := context .selectedPeer .TotalCountByStore (store .GetID ())
439+ storeCount := context .selectedPeer .Get (store .GetID (), group )
440+ if store .GetID () == peer .GetId () {
441+ originStorePickedCount = storeCount
442+ }
450443 // If storeCount is equal to the maxStoreTotalCount, we should skip this store as candidate.
451444 // If the storeCount are all the same for the whole cluster(maxStoreTotalCount == minStoreTotalCount), any store
452445 // could be selected as candidate.
453446 if storeCount < maxStoreTotalCount || maxStoreTotalCount == minStoreTotalCount {
454447 if filter .Target (r .cluster .GetOpts (), store , filters ) {
455- candidates = append (candidates , store .GetID ())
448+ if storeCount < minCount {
449+ minCount = storeCount
450+ newPeer = & metapb.Peer {
451+ StoreId : store .GetID (),
452+ Role : peer .GetRole (),
453+ }
454+ }
456455 }
457456 }
458457 }
459- return candidates
460- }
461-
462- func (r * RegionScatterer ) selectStore (group string , peer * metapb.Peer , sourceStoreID uint64 , candidates []uint64 , context engineContext ) * metapb.Peer {
463- if len (candidates ) < 1 {
458+ if originStorePickedCount <= minCount {
464459 return peer
465460 }
466- var newPeer * metapb.Peer
467- minCount := uint64 (math .MaxUint64 )
468- for _ , storeID := range candidates {
469- count := context .selectedPeer .Get (storeID , group )
470- if count < minCount {
471- minCount = count
472- newPeer = & metapb.Peer {
473- StoreId : storeID ,
474- Role : peer .GetRole (),
475- }
476- }
477- }
478- // if the source store have the least count, we don't need to scatter this peer
479- for _ , storeID := range candidates {
480- if storeID == sourceStoreID && context .selectedPeer .Get (sourceStoreID , group ) <= minCount {
481- return peer
482- }
483- }
484461 if newPeer == nil {
485462 return peer
486463 }
@@ -489,11 +466,12 @@ func (r *RegionScatterer) selectStore(group string, peer *metapb.Peer, sourceSto
489466
490467// selectAvailableLeaderStore select the target leader store from the candidates. The candidates would be collected by
491468// the existed peers store depended on the leader counts in the group level. Please use this func before scatter spacial engines.
492- func (r * RegionScatterer ) selectAvailableLeaderStore (group string , region * core.RegionInfo , leaderCandidateStores []uint64 , context engineContext ) uint64 {
469+ func (r * RegionScatterer ) selectAvailableLeaderStore (group string , region * core.RegionInfo ,
470+ leaderCandidateStores []uint64 , context engineContext ) (leaderID uint64 , leaderStorePickedCount uint64 ) {
493471 sourceStore := r .cluster .GetStore (region .GetLeader ().GetStoreId ())
494472 if sourceStore == nil {
495473 log .Error ("failed to get the store" , zap .Uint64 ("store-id" , region .GetLeader ().GetStoreId ()), errs .ZapError (errs .ErrGetSourceStore ))
496- return 0
474+ return 0 , 0
497475 }
498476 minStoreGroupLeader := uint64 (math .MaxUint64 )
499477 id := uint64 (0 )
@@ -508,7 +486,7 @@ func (r *RegionScatterer) selectAvailableLeaderStore(group string, region *core.
508486 id = storeID
509487 }
510488 }
511- return id
489+ return id , minStoreGroupLeader
512490}
513491
514492// Put put the final distribution in the context no matter the operator was created
0 commit comments