func NewRegionCache(pdClient pd.Client) *RegionCache {
	c := &RegionCache{
		pdClient: pdClient,
	}
	c.mu.regions = make(map[RegionVerID]*Region)
	c.mu.latestVersions = make(map[uint64]RegionVerID)
	c.mu.sorted = btree.New(btreeDegree)
	c.storeMu.stores = make(map[uint64]*Store)
	c.notifyCheckCh = make(chan struct{}, 1)
	c.closeCh = make(chan struct{})
	interval := config.GetGlobalConfig().StoresRefreshInterval
	go c.asyncCheckAndResolveLoop(time.Duration(interval) * time.Second)
	c.enableForwarding = config.GetGlobalConfig().EnableForwarding
	return c
}
NewRegionCache creates a RegionCache.
package locate
import (
	"bytes"
	"context"
	"fmt"
	"math/rand"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"
	"github.com/gogo/protobuf/proto"
	"github.com/google/btree"
	"github.com/opentracing/opentracing-go"
	"github.com/pingcap/errors"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/tikv/client-go/v2/config"
	tikverr "github.com/tikv/client-go/v2/error"
	"github.com/tikv/client-go/v2/internal/client"
	"github.com/tikv/client-go/v2/internal/logutil"
	"github.com/tikv/client-go/v2/internal/retry"
	"github.com/tikv/client-go/v2/kv"
	"github.com/tikv/client-go/v2/metrics"
	"github.com/tikv/client-go/v2/tikvrpc"
	"github.com/tikv/client-go/v2/util"
	pd "github.com/tikv/pd/client"
	atomic2 "go.uber.org/atomic"
	"go.uber.org/zap"
	"golang.org/x/sync/singleflight"
	"google.golang.org/grpc"
	"google.golang.org/grpc/backoff"
	"google.golang.org/grpc/credentials"
	healthpb "google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/keepalive"
)
const (
	btreeDegree               = 32
	invalidatedLastAccessTime = -1
	defaultRegionsPerBatch    = 128
)
var regionCacheTTLSec int64 = 600
func SetRegionCacheTTLSec(t int64) {
	regionCacheTTLSec = t
}
const (
	updated  int32 = iota  
	needSync               
)
type InvalidReason int32
const (
	Ok InvalidReason = iota
	NoLeader
	RegionNotFound
	EpochNotMatch
	StoreNotFound
	Other
)
type Region struct {
	meta          *metapb.Region  
	store         unsafe.Pointer  
	syncFlag      int32           
	lastAccess    int64           
	invalidReason InvalidReason   
}
type AccessIndex int
type regionStore struct {
	workTiKVIdx    AccessIndex           
	proxyTiKVIdx   AccessIndex           
	workTiFlashIdx int32                 
	stores         []*Store              
	storeEpochs    []uint32              
	accessIndex    [numAccessMode][]int  
}
func (r *regionStore) accessStore(mode accessMode, idx AccessIndex) (int, *Store) {
	sidx := r.accessIndex[mode][idx]
	return sidx, r.stores[sidx]
}
func (r *regionStore) getAccessIndex(mode accessMode, store *Store) AccessIndex {
	for index, sidx := range r.accessIndex[mode] {
		if r.stores[sidx].storeID == store.storeID {
			return AccessIndex(index)
		}
	}
	return -1
}
func (r *regionStore) accessStoreNum(mode accessMode) int {
	return len(r.accessIndex[mode])
}
func (r *regionStore) clone() *regionStore {
	storeEpochs := make([]uint32, len(r.stores))
	rs := &regionStore{
		workTiFlashIdx: r.workTiFlashIdx,
		proxyTiKVIdx:   r.proxyTiKVIdx,
		workTiKVIdx:    r.workTiKVIdx,
		stores:         r.stores,
		storeEpochs:    storeEpochs,
	}
	copy(storeEpochs, r.storeEpochs)
	for i := 0; i < int(numAccessMode); i++ {
		rs.accessIndex[i] = make([]int, len(r.accessIndex[i]))
		copy(rs.accessIndex[i], r.accessIndex[i])
	}
	return rs
}
func (r *regionStore) follower(seed uint32, op *storeSelectorOp) AccessIndex {
	l := uint32(r.accessStoreNum(tiKVOnly))
	if l <= 1 {
		return r.workTiKVIdx
	}
	for retry := l - 1; retry > 0; retry-- {
		followerIdx := AccessIndex(seed % (l - 1))
		if followerIdx >= r.workTiKVIdx {
			followerIdx++
		}
		storeIdx, s := r.accessStore(tiKVOnly, followerIdx)
		if r.storeEpochs[storeIdx] == atomic.LoadUint32(&s.epoch) && r.filterStoreCandidate(followerIdx, op) {
			return followerIdx
		}
		seed++
	}
	return r.workTiKVIdx
}
func (r *regionStore) kvPeer(seed uint32, op *storeSelectorOp) AccessIndex {
	if op.leaderOnly {
		return r.workTiKVIdx
	}
	candidates := make([]AccessIndex, 0, r.accessStoreNum(tiKVOnly))
	for i := 0; i < r.accessStoreNum(tiKVOnly); i++ {
		accessIdx := AccessIndex(i)
		storeIdx, s := r.accessStore(tiKVOnly, accessIdx)
		if r.storeEpochs[storeIdx] != atomic.LoadUint32(&s.epoch) || !r.filterStoreCandidate(accessIdx, op) {
			continue
		}
		candidates = append(candidates, accessIdx)
	}
	if len(candidates) == 0 {
		return r.workTiKVIdx
	}
	return candidates[seed%uint32(len(candidates))]
}
func (r *regionStore) filterStoreCandidate(aidx AccessIndex, op *storeSelectorOp) bool {
	_, s := r.accessStore(tiKVOnly, aidx)
	return s.IsLabelsMatch(op.labels)
}
func (r *Region) init(bo *retry.Backoffer, c *RegionCache) error {
	rs := &regionStore{
		workTiKVIdx:    0,
		proxyTiKVIdx:   -1,
		workTiFlashIdx: 0,
		stores:         make([]*Store, 0, len(r.meta.Peers)),
		storeEpochs:    make([]uint32, 0, len(r.meta.Peers)),
	}
	availablePeers := r.meta.GetPeers()[:0]
	for _, p := range r.meta.Peers {
		c.storeMu.RLock()
		store, exists := c.storeMu.stores[p.StoreId]
		c.storeMu.RUnlock()
		if !exists {
			store = c.getStoreByStoreID(p.StoreId)
		}
		addr, err := store.initResolve(bo, c)
		if err != nil {
			return err
		}
		if addr == "" {
			continue
		}
		availablePeers = append(availablePeers, p)
		switch store.storeType {
		case tikvrpc.TiKV:
			rs.accessIndex[tiKVOnly] = append(rs.accessIndex[tiKVOnly], len(rs.stores))
		case tikvrpc.TiFlash:
			rs.accessIndex[tiFlashOnly] = append(rs.accessIndex[tiFlashOnly], len(rs.stores))
		}
		rs.stores = append(rs.stores, store)
		rs.storeEpochs = append(rs.storeEpochs, atomic.LoadUint32(&store.epoch))
	}
	if len(availablePeers) == 0 {
		return errors.Errorf("no available peers, region: {%v}", r.meta)
	}
	r.meta.Peers = availablePeers
	atomic.StorePointer(&r.store, unsafe.Pointer(rs))
	r.lastAccess = time.Now().Unix()
	return nil
}
func (r *Region) getStore() (store *regionStore) {
	store = (*regionStore)(atomic.LoadPointer(&r.store))
	return
}
func (r *Region) compareAndSwapStore(oldStore, newStore *regionStore) bool {
	return atomic.CompareAndSwapPointer(&r.store, unsafe.Pointer(oldStore), unsafe.Pointer(newStore))
}
func (r *Region) checkRegionCacheTTL(ts int64) bool {
	if _, err := util.EvalFailpoint("invalidateRegionCache"); err == nil {
		r.invalidate(Other)
	}
	for {
		lastAccess := atomic.LoadInt64(&r.lastAccess)
		if ts-lastAccess > regionCacheTTLSec {
			return false
		}
		if atomic.CompareAndSwapInt64(&r.lastAccess, lastAccess, ts) {
			return true
		}
	}
}
func (r *Region) invalidate(reason InvalidReason) {
	metrics.RegionCacheCounterWithInvalidateRegionFromCacheOK.Inc()
	atomic.StoreInt32((*int32)(&r.invalidReason), int32(reason))
	atomic.StoreInt64(&r.lastAccess, invalidatedLastAccessTime)
}
func (r *Region) scheduleReload() {
	oldValue := atomic.LoadInt32(&r.syncFlag)
	if oldValue != updated {
		return
	}
	atomic.CompareAndSwapInt32(&r.syncFlag, oldValue, needSync)
}
func (r *Region) checkNeedReloadAndMarkUpdated() bool {
	oldValue := atomic.LoadInt32(&r.syncFlag)
	if oldValue == updated {
		return false
	}
	return atomic.CompareAndSwapInt32(&r.syncFlag, oldValue, updated)
}
func (r *Region) checkNeedReload() bool {
	v := atomic.LoadInt32(&r.syncFlag)
	return v != updated
}
func (r *Region) isValid() bool {
	return r != nil && !r.checkNeedReload() && r.checkRegionCacheTTL(time.Now().Unix())
}
type RegionCache struct {
	pdClient         pd.Client
	enableForwarding bool
	mu struct {
		sync.RWMutex                            
		regions        map[RegionVerID]*Region  
		latestVersions map[uint64]RegionVerID   
		sorted         *btree.BTree             
	}
	storeMu struct {
		sync.RWMutex
		stores map[uint64]*Store
	}
	notifyCheckCh chan struct{}
	closeCh       chan struct{}
	testingKnobs struct {
		mockRequestLiveness func(s *Store, bo *retry.Backoffer) livenessState
	}
}