func (r *RegionsInfo) GetStoreFollowerCount(storeID uint64) int {
	return r.followers[storeID].length()
}
GetStoreFollowerCount get the total count of a store's follower RegionInfo
package core
import (
	"bytes"
	"encoding/hex"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"unsafe"
	"github.com/gogo/protobuf/proto"
	"github.com/pingcap/errors"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/kvproto/pkg/pdpb"
	"github.com/pingcap/kvproto/pkg/replication_modepb"
	"github.com/pingcap/log"
	"github.com/tikv/pd/pkg/logutil"
	"go.uber.org/zap"
)
func errRegionIsStale(region *metapb.Region, origin *metapb.Region) error {
	return errors.Errorf("region is stale: region %v origin %v", region, origin)
}
type RegionInfo struct {
	term              uint64
	meta              *metapb.Region
	learners          []*metapb.Peer
	voters            []*metapb.Peer
	leader            *metapb.Peer
	downPeers         []*pdpb.PeerStats
	pendingPeers      []*metapb.Peer
	writtenBytes      uint64
	writtenKeys       uint64
	readBytes         uint64
	readKeys          uint64
	approximateSize   int64
	approximateKeys   int64
	interval          *pdpb.TimeInterval
	replicationStatus *replication_modepb.RegionReplicationStatus
	QueryStats        *pdpb.QueryStats
	flowRoundDivisor  uint64
}
func NewRegionInfo(region *metapb.Region, leader *metapb.Peer, opts ...RegionCreateOption) *RegionInfo {
	regionInfo := &RegionInfo{
		meta:   region,
		leader: leader,
	}
	for _, opt := range opts {
		opt(regionInfo)
	}
	classifyVoterAndLearner(regionInfo)
	return regionInfo
}
func classifyVoterAndLearner(region *RegionInfo) {
	learners := make([]*metapb.Peer, 0, 1)
	voters := make([]*metapb.Peer, 0, len(region.meta.Peers))
	for _, p := range region.meta.Peers {
		if IsLearner(p) {
			learners = append(learners, p)
		} else {
			voters = append(voters, p)
		}
	}
	sort.Sort(peerSlice(learners))
	sort.Sort(peerSlice(voters))
	region.learners = learners
	region.voters = voters
}
const (
	EmptyRegionApproximateSize = 1
	ImpossibleFlowSize = 1 << 50
	statsReportMinInterval = 3       
	statsReportMaxInterval = 5 * 60  
)
func RegionFromHeartbeat(heartbeat *pdpb.RegionHeartbeatRequest, opts ...RegionCreateOption) *RegionInfo {
	regionSize := heartbeat.GetApproximateSize() / (1 << 20)
	if regionSize < EmptyRegionApproximateSize {
		regionSize = EmptyRegionApproximateSize
	}
	region := &RegionInfo{
		term:              heartbeat.GetTerm(),
		meta:              heartbeat.GetRegion(),
		leader:            heartbeat.GetLeader(),
		downPeers:         heartbeat.GetDownPeers(),
		pendingPeers:      heartbeat.GetPendingPeers(),
		writtenBytes:      heartbeat.GetBytesWritten(),
		writtenKeys:       heartbeat.GetKeysWritten(),
		readBytes:         heartbeat.GetBytesRead(),
		readKeys:          heartbeat.GetKeysRead(),
		approximateSize:   int64(regionSize),
		approximateKeys:   int64(heartbeat.GetApproximateKeys()),
		interval:          heartbeat.GetInterval(),
		replicationStatus: heartbeat.GetReplicationStatus(),
		QueryStats:        heartbeat.GetQueryStats(),
	}
	for _, opt := range opts {
		opt(region)
	}
	if region.writtenKeys >= ImpossibleFlowSize || region.writtenBytes >= ImpossibleFlowSize {
		region.writtenKeys = 0
		region.writtenBytes = 0
	}
	if region.readKeys >= ImpossibleFlowSize || region.readBytes >= ImpossibleFlowSize {
		region.readKeys = 0
		region.readBytes = 0
	}
	sort.Sort(peerStatsSlice(region.downPeers))
	sort.Sort(peerSlice(region.pendingPeers))
	classifyVoterAndLearner(region)
	return region
}
func (r *RegionInfo) Clone(opts ...RegionCreateOption) *RegionInfo {
	downPeers := make([]*pdpb.PeerStats, 0, len(r.downPeers))
	for _, peer := range r.downPeers {
		downPeers = append(downPeers, proto.Clone(peer).(*pdpb.PeerStats))
	}
	pendingPeers := make([]*metapb.Peer, 0, len(r.pendingPeers))
	for _, peer := range r.pendingPeers {
		pendingPeers = append(pendingPeers, proto.Clone(peer).(*metapb.Peer))
	}
	region := &RegionInfo{
		term:              r.term,
		meta:              proto.Clone(r.meta).(*metapb.Region),
		leader:            proto.Clone(r.leader).(*metapb.Peer),
		downPeers:         downPeers,
		pendingPeers:      pendingPeers,
		writtenBytes:      r.writtenBytes,
		writtenKeys:       r.writtenKeys,
		readBytes:         r.readBytes,
		readKeys:          r.readKeys,
		approximateSize:   r.approximateSize,
		approximateKeys:   r.approximateKeys,
		interval:          proto.Clone(r.interval).(*pdpb.TimeInterval),
		replicationStatus: r.replicationStatus,
	}
	for _, opt := range opts {
		opt(region)
	}
	classifyVoterAndLearner(region)
	return region
}
func (r *RegionInfo) GetTerm() uint64 {
	return r.term
}
func (r *RegionInfo) GetLearners() []*metapb.Peer {
	return r.learners
}
func (r *RegionInfo) GetVoters() []*metapb.Peer {
	return r.voters
}
func (r *RegionInfo) GetPeer(peerID uint64) *metapb.Peer {
	for _, peer := range r.meta.GetPeers() {
		if peer.GetId() == peerID {
			return peer
		}
	}
	return nil
}
func (r *RegionInfo) GetDownPeer(peerID uint64) *metapb.Peer {
	for _, down := range r.downPeers {
		if down.GetPeer().GetId() == peerID {
			return down.GetPeer()
		}
	}
	return nil
}
func (r *RegionInfo) GetDownVoter(peerID uint64) *metapb.Peer {
	for _, down := range r.downPeers {
		if down.GetPeer().GetId() == peerID && !IsLearner(down.GetPeer()) {
			return down.GetPeer()
		}
	}
	return nil
}
func (r *RegionInfo) GetDownLearner(peerID uint64) *metapb.Peer {
	for _, down := range r.downPeers {
		if down.GetPeer().GetId() == peerID && IsLearner(down.GetPeer()) {
			return down.GetPeer()
		}
	}
	return nil
}
func (r *RegionInfo) GetPendingPeer(peerID uint64) *metapb.Peer {
	for _, peer := range r.pendingPeers {
		if peer.GetId() == peerID {
			return peer
		}
	}
	return nil
}
func (r *RegionInfo) GetPendingVoter(peerID uint64) *metapb.Peer {
	for _, peer := range r.pendingPeers {
		if peer.GetId() == peerID && !IsLearner(peer) {
			return peer
		}
	}
	return nil
}
func (r *RegionInfo) GetPendingLearner(peerID uint64) *metapb.Peer {
	for _, peer := range r.pendingPeers {
		if peer.GetId() == peerID && IsLearner(peer) {
			return peer
		}
	}
	return nil
}
func (r *RegionInfo) GetStorePeer(storeID uint64) *metapb.Peer {
	for _, peer := range r.meta.GetPeers() {
		if peer.GetStoreId() == storeID {
			return peer
		}
	}
	return nil
}
func (r *RegionInfo) GetStoreVoter(storeID uint64) *metapb.Peer {
	for _, peer := range r.voters {
		if peer.GetStoreId() == storeID {
			return peer
		}
	}
	return nil
}
func (r *RegionInfo) GetStoreLearner(storeID uint64) *metapb.Peer {
	for _, peer := range r.learners {
		if peer.GetStoreId() == storeID {
			return peer
		}
	}
	return nil
}
func (r *RegionInfo) GetStoreIds() map[uint64]struct{} {
	peers := r.meta.GetPeers()
	stores := make(map[uint64]struct{}, len(peers))
	for _, peer := range peers {
		stores[peer.GetStoreId()] = struct{}{}
	}
	return stores
}
func (r *RegionInfo) GetFollowers() map[uint64]*metapb.Peer {
	peers := r.GetVoters()
	followers := make(map[uint64]*metapb.Peer, len(peers))
	for _, peer := range peers {
		if r.leader == nil || r.leader.GetId() != peer.GetId() {
			followers[peer.GetStoreId()] = peer
		}
	}
	return followers
}
func (r *RegionInfo) GetFollower() *metapb.Peer {
	for _, peer := range r.GetVoters() {
		if r.leader == nil || r.leader.GetId() != peer.GetId() {
			return peer
		}
	}
	return nil
}
func (r *RegionInfo) GetDiffFollowers(other *RegionInfo) []*metapb.Peer {
	res := make([]*metapb.Peer, 0, len(r.meta.Peers))
	for _, p := range r.GetFollowers() {
		diff := true
		for _, o := range other.GetFollowers() {
			if p.GetStoreId() == o.GetStoreId() {
				diff = false
				break
			}
		}
		if diff {
			res = append(res, p)
		}
	}
	return res
}
func (r *RegionInfo) GetID() uint64 {
	return r.meta.GetId()
}
func (r *RegionInfo) GetMeta() *metapb.Region {
	if r == nil {
		return nil
	}
	return r.meta
}
func (r *RegionInfo) GetStat() *pdpb.RegionStat {
	if r == nil {
		return nil
	}
	return &pdpb.RegionStat{
		BytesWritten: r.writtenBytes,
		BytesRead:    r.readBytes,
		KeysWritten:  r.writtenKeys,
		KeysRead:     r.readKeys,
	}
}
func (r *RegionInfo) GetApproximateSize() int64 {
	return r.approximateSize
}
func (r *RegionInfo) GetApproximateKeys() int64 {
	return r.approximateKeys
}
func (r *RegionInfo) GetInterval() *pdpb.TimeInterval {
	return r.interval
}
func (r *RegionInfo) GetDownPeers() []*pdpb.PeerStats {
	return r.downPeers
}
func (r *RegionInfo) GetPendingPeers() []*metapb.Peer {
	return r.pendingPeers
}
func (r *RegionInfo) GetBytesRead() uint64 {
	return r.readBytes
}
func (r *RegionInfo) GetRoundBytesRead() uint64 {
	if r.flowRoundDivisor == 0 {
		return r.readBytes
	}
	return ((r.readBytes + r.flowRoundDivisor/2) / r.flowRoundDivisor) * r.flowRoundDivisor
}
func (r *RegionInfo) GetBytesWritten() uint64 {
	return r.writtenBytes
}
func (r *RegionInfo) GetRoundBytesWritten() uint64 {
	if r.flowRoundDivisor == 0 {
		return r.writtenBytes
	}
	return ((r.writtenBytes + r.flowRoundDivisor/2) / r.flowRoundDivisor) * r.flowRoundDivisor
}
func (r *RegionInfo) GetKeysWritten() uint64 {
	return r.writtenKeys
}
func (r *RegionInfo) GetKeysRead() uint64 {
	return r.readKeys
}
func (r *RegionInfo) GetWriteRate() (bytesRate, keysRate float64) {
	reportInterval := r.GetInterval()
	interval := reportInterval.GetEndTimestamp() - reportInterval.GetStartTimestamp()
	if interval >= statsReportMinInterval && interval <= statsReportMaxInterval {
		return float64(r.writtenBytes) / float64(interval), float64(r.writtenKeys) / float64(interval)
	}
	return 0, 0
}
func (r *RegionInfo) GetLeader() *metapb.Peer {
	return r.leader
}
func (r *RegionInfo) GetStartKey() []byte {
	return r.meta.StartKey
}
func (r *RegionInfo) GetEndKey() []byte {
	return r.meta.EndKey
}
func (r *RegionInfo) GetPeers() []*metapb.Peer {
	return r.meta.GetPeers()
}
func (r *RegionInfo) GetRegionEpoch() *metapb.RegionEpoch {
	return r.meta.RegionEpoch
}
func (r *RegionInfo) GetReplicationStatus() *replication_modepb.RegionReplicationStatus {
	return r.replicationStatus
}
type RegionGuideFunc func(region, origin *RegionInfo) (isNew, saveKV, saveCache, needSync bool)
func GenerateRegionGuideFunc(enableLog bool) RegionGuideFunc {
	noLog := func(msg string, fields ...zap.Field) {}
	debug, info := noLog, noLog
	if enableLog {
		debug = log.Debug
		info = log.Info
	}
	return func(region, origin *RegionInfo) (isNew, saveKV, saveCache, needSync bool) {
		if origin == nil {
			debug("insert new region",
				zap.Uint64("region-id", region.GetID()),
				logutil.ZapRedactStringer("meta-region", RegionToHexMeta(region.GetMeta())))
			saveKV, saveCache, isNew = true, true, true
		} else {
			r := region.GetRegionEpoch()
			o := origin.GetRegionEpoch()
			if r.GetVersion() > o.GetVersion() {
				info("region Version changed",
					zap.Uint64("region-id", region.GetID()),
					logutil.ZapRedactString("detail", DiffRegionKeyInfo(origin, region)),
					zap.Uint64("old-version", o.GetVersion()),
					zap.Uint64("new-version", r.GetVersion()),
				)
				saveKV, saveCache = true, true
			}
			if r.GetConfVer() > o.GetConfVer() {
				info("region ConfVer changed",
					zap.Uint64("region-id", region.GetID()),
					zap.String("detail", DiffRegionPeersInfo(origin, region)),
					zap.Uint64("old-confver", o.GetConfVer()),
					zap.Uint64("new-confver", r.GetConfVer()),
				)
				saveKV, saveCache = true, true
			}
			if region.GetLeader().GetId() != origin.GetLeader().GetId() {
				if origin.GetLeader().GetId() == 0 {
					isNew = true
				} else {
					info("leader changed",
						zap.Uint64("region-id", region.GetID()),
						zap.Uint64("from", origin.GetLeader().GetStoreId()),
						zap.Uint64("to", region.GetLeader().GetStoreId()),
					)
				}
				saveCache, needSync = true, true
			}
			if !SortedPeersStatsEqual(region.GetDownPeers(), origin.GetDownPeers()) {
				debug("down-peers changed", zap.Uint64("region-id", region.GetID()))
				saveCache, needSync = true, true
			}
			if !SortedPeersEqual(region.GetPendingPeers(), origin.GetPendingPeers()) {
				debug("pending-peers changed", zap.Uint64("region-id", region.GetID()))
				saveCache, needSync = true, true
			}
			if len(region.GetPeers()) != len(origin.GetPeers()) {
				saveKV, saveCache = true, true
			}
			if region.GetApproximateSize() != origin.GetApproximateSize() ||
				region.GetApproximateKeys() != origin.GetApproximateKeys() {
				saveCache = true
			}
			if region.GetRoundBytesWritten() != origin.GetRoundBytesWritten() ||
				region.GetRoundBytesRead() != origin.GetRoundBytesRead() {
				saveCache, needSync = true, true
			}
			if region.GetReplicationStatus().GetState() != replication_modepb.RegionReplicationState_UNKNOWN &&
				(region.GetReplicationStatus().GetState() != origin.GetReplicationStatus().GetState() ||
					region.GetReplicationStatus().GetStateId() != origin.GetReplicationStatus().GetStateId()) {
				saveCache = true
			}
		}
		return
	}
}
type regionMap map[uint64]*regionItem
func newRegionMap() regionMap {
	return make(map[uint64]*regionItem)
}
func (rm regionMap) Len() int {
	return len(rm)
}
func (rm regionMap) Get(id uint64) *regionItem {
	return rm[id]
}
func (rm regionMap) AddNew(region *RegionInfo) *regionItem {
	item := &regionItem{region: region}
	rm[region.GetID()] = item
	return item
}
func (rm regionMap) Delete(id uint64) {
	delete(rm, id)
}
type RegionsInfo struct {
	tree         *regionTree
	regions      regionMap               
	leaders      map[uint64]*regionTree  
	followers    map[uint64]*regionTree  
	learners     map[uint64]*regionTree  
	pendingPeers map[uint64]*regionTree  
}
func NewRegionsInfo() *RegionsInfo {
	return &RegionsInfo{
		tree:         newRegionTree(),
		regions:      newRegionMap(),
		leaders:      make(map[uint64]*regionTree),
		followers:    make(map[uint64]*regionTree),
		learners:     make(map[uint64]*regionTree),
		pendingPeers: make(map[uint64]*regionTree),
	}
}
func (r *RegionsInfo) GetRegion(regionID uint64) *RegionInfo {
	if item := r.regions.Get(regionID); item != nil {
		return item.region
	}
	return nil
}
func (r *RegionsInfo) SetRegion(region *RegionInfo) (overlaps []*RegionInfo) {
	var item *regionItem    
	var origin *RegionInfo  
	var rangeChanged bool   
	var peersChanged bool   
	if item = r.regions.Get(region.GetID()); item != nil {
		origin = item.region
		rangeChanged = !bytes.Equal(origin.GetStartKey(), region.GetStartKey()) ||
			!bytes.Equal(origin.GetEndKey(), region.GetEndKey())
		if rangeChanged {
			r.tree.remove(origin)
			peersChanged = true
		} else {
			peersChanged = r.shouldRemoveFromSubTree(region, origin)
		}
		if peersChanged {
			r.removeRegionFromSubTree(origin)
		}
		item.region = region
	} else {
		rangeChanged = true
		peersChanged = true
		item = r.regions.AddNew(region)
	}
	if !rangeChanged {
		r.tree.updateStat(origin, region)
	} else {
		overlaps = r.tree.update(item)
		for _, old := range overlaps {
			r.RemoveRegion(r.GetRegion(old.GetID()))
		}
	}
	if !peersChanged {
		r.updateSubTreeStat(origin, region)
	} else {
		for _, peer := range region.GetVoters() {
			storeID := peer.GetStoreId()
			if peer.GetId() == region.leader.GetId() {
				store, ok := r.leaders[storeID]
				if !ok {
					store = newRegionTree()
					r.leaders[storeID] = store
				}
				store.update(item)
			} else {
				store, ok := r.followers[storeID]
				if !ok {
					store = newRegionTree()
					r.followers[storeID] = store
				}
				store.update(item)
			}
		}
		for _, peer := range region.GetLearners() {
			storeID := peer.GetStoreId()
			store, ok := r.learners[storeID]
			if !ok {
				store = newRegionTree()
				r.learners[storeID] = store
			}
			store.update(item)
		}
		for _, peer := range region.GetPendingPeers() {
			storeID := peer.GetStoreId()
			store, ok := r.pendingPeers[storeID]
			if !ok {
				store = newRegionTree()
				r.pendingPeers[storeID] = store
			}
			store.update(item)
		}
	}
	return
}
func (r *RegionsInfo) Len() int {
	return r.regions.Len()
}
func (r *RegionsInfo) TreeLen() int {
	return r.tree.length()
}
func (r *RegionsInfo) updateSubTreeStat(origin *RegionInfo, region *RegionInfo) {
	for _, peer := range region.GetVoters() {
		storeID := peer.GetStoreId()
		if peer.GetId() == region.leader.GetId() {
			if tree, ok := r.leaders[storeID]; ok {
				tree.updateStat(origin, region)
			}
		} else {
			if tree, ok := r.followers[storeID]; ok {
				tree.updateStat(origin, region)
			}
		}
	}
	for _, peer := range region.GetLearners() {
		if tree, ok := r.learners[peer.GetStoreId()]; ok {
			tree.updateStat(origin, region)
		}
	}
	for _, peer := range region.GetPendingPeers() {
		if tree, ok := r.pendingPeers[peer.GetStoreId()]; ok {
			tree.updateStat(origin, region)
		}
	}
}
func (r *RegionsInfo) GetOverlaps(region *RegionInfo) []*RegionInfo {
	return r.tree.getOverlaps(region)
}
func (r *RegionsInfo) RemoveRegion(region *RegionInfo) {
	r.tree.remove(region)
	r.regions.Delete(region.GetID())
	r.removeRegionFromSubTree(region)
}
func (r *RegionsInfo) removeRegionFromSubTree(region *RegionInfo) {
	for _, peer := range region.meta.GetPeers() {
		storeID := peer.GetStoreId()
		r.leaders[storeID].remove(region)
		r.followers[storeID].remove(region)
		r.learners[storeID].remove(region)
		r.pendingPeers[storeID].remove(region)
	}
}
type peerSlice []*metapb.Peer
func (s peerSlice) Len() int {
	return len(s)
}
func (s peerSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s peerSlice) Less(i, j int) bool {
	return s[i].GetId() < s[j].GetId()
}
func SortedPeersEqual(peersA, peersB []*metapb.Peer) bool {
	if len(peersA) != len(peersB) {
		return false
	}
	for i, peerA := range peersA {
		peerB := peersB[i]
		if peerA.GetStoreId() != peerB.GetStoreId() || peerA.GetId() != peerB.GetId() {
			return false
		}
	}
	return true
}
type peerStatsSlice []*pdpb.PeerStats
func (s peerStatsSlice) Len() int {
	return len(s)
}
func (s peerStatsSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s peerStatsSlice) Less(i, j int) bool {
	return s[i].GetPeer().GetId() < s[j].GetPeer().GetId()
}
func SortedPeersStatsEqual(peersA, peersB []*pdpb.PeerStats) bool {
	if len(peersA) != len(peersB) {
		return false
	}
	for i, peerStatsA := range peersA {
		peerA := peerStatsA.GetPeer()
		peerB := peersB[i].GetPeer()
		if peerA.GetStoreId() != peerB.GetStoreId() || peerA.GetId() != peerB.GetId() {
			return false
		}
	}
	return true
}
func (r *RegionsInfo) shouldRemoveFromSubTree(region *RegionInfo, origin *RegionInfo) bool {
	return origin.leader.GetId() != region.leader.GetId() ||
		!SortedPeersEqual(origin.GetVoters(), region.GetVoters()) ||
		!SortedPeersEqual(origin.GetLearners(), region.GetLearners()) ||
		!SortedPeersEqual(origin.GetPendingPeers(), region.GetPendingPeers())
}
func (r *RegionsInfo) SearchRegion(regionKey []byte) *RegionInfo {
	region := r.tree.search(regionKey)
	if region == nil {
		return nil
	}
	return r.GetRegion(region.GetID())
}
func (r *RegionsInfo) SearchPrevRegion(regionKey []byte) *RegionInfo {
	region := r.tree.searchPrev(regionKey)
	if region == nil {
		return nil
	}
	return r.GetRegion(region.GetID())
}
func (r *RegionsInfo) GetRegions() []*RegionInfo {
	regions := make([]*RegionInfo, 0, r.regions.Len())
	for _, item := range r.regions {
		regions = append(regions, item.region)
	}
	return regions
}
func (r *RegionsInfo) GetStoreRegions(storeID uint64) []*RegionInfo {
	regions := make([]*RegionInfo, 0, r.GetStoreRegionCount(storeID))
	if leaders, ok := r.leaders[storeID]; ok {
		regions = append(regions, leaders.scanRanges()...)
	}
	if followers, ok := r.followers[storeID]; ok {
		regions = append(regions, followers.scanRanges()...)
	}
	if learners, ok := r.learners[storeID]; ok {
		regions = append(regions, learners.scanRanges()...)
	}
	return regions
}
func (r *RegionsInfo) GetStoreLeaderRegionSize(storeID uint64) int64 {
	return r.leaders[storeID].TotalSize()
}
func (r *RegionsInfo) GetStoreFollowerRegionSize(storeID uint64) int64 {
	return r.followers[storeID].TotalSize()
}
func (r *RegionsInfo) GetStoreLearnerRegionSize(storeID uint64) int64 {
	return r.learners[storeID].TotalSize()
}
func (r *RegionsInfo) GetStoreRegionSize(storeID uint64) int64 {
	return r.GetStoreLeaderRegionSize(storeID) + r.GetStoreFollowerRegionSize(storeID) + r.GetStoreLearnerRegionSize(storeID)
}
func (r *RegionsInfo) GetStoreLeaderWriteRate(storeID uint64) (bytesRate, keysRate float64) {
	return r.leaders[storeID].TotalWriteRate()
}
func (r *RegionsInfo) GetStoreWriteRate(storeID uint64) (bytesRate, keysRate float64) {
	storeBytesRate, storeKeysRate := r.leaders[storeID].TotalWriteRate()
	bytesRate += storeBytesRate
	keysRate += storeKeysRate
	storeBytesRate, storeKeysRate = r.followers[storeID].TotalWriteRate()
	bytesRate += storeBytesRate
	keysRate += storeKeysRate
	storeBytesRate, storeKeysRate = r.learners[storeID].TotalWriteRate()
	bytesRate += storeBytesRate
	keysRate += storeKeysRate
	return
}
func (r *RegionsInfo) GetMetaRegions() []*metapb.Region {
	regions := make([]*metapb.Region, 0, r.regions.Len())
	for _, item := range r.regions {
		regions = append(regions, proto.Clone(item.region.meta).(*metapb.Region))
	}
	return regions
}
func (r *RegionsInfo) GetRegionCount() int {
	return r.regions.Len()
}
func (r *RegionsInfo) GetStoreRegionCount(storeID uint64) int {
	return r.GetStoreLeaderCount(storeID) + r.GetStoreFollowerCount(storeID) + r.GetStoreLearnerCount(storeID)
}
func (r *RegionsInfo) GetStorePendingPeerCount(storeID uint64) int {
	return r.pendingPeers[storeID].length()
}
func (r *RegionsInfo) GetStoreLeaderCount(storeID uint64) int {
	return r.leaders[storeID].length()
}