func NewRegionScoreFilter(scope string, source *core.StoreInfo, opt *config.PersistOptions) Filter {
	return &RegionScoreFilter{
		scope: scope,
		score: source.RegionScore(opt.GetRegionScoreFormulaVersion(), opt.GetHighSpaceRatio(), opt.GetLowSpaceRatio(), 0),
	}
}
NewRegionScoreFilter creates a Filter that filters all high score stores.
package filter
import (
	"fmt"
	"strconv"
	"github.com/golang/protobuf/proto"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/log"
	"github.com/tikv/pd/pkg/slice"
	"github.com/tikv/pd/server/config"
	"github.com/tikv/pd/server/core"
	"github.com/tikv/pd/server/core/storelimit"
	"github.com/tikv/pd/server/schedule/opt"
	"github.com/tikv/pd/server/schedule/placement"
	"go.uber.org/zap"
)
func SelectSourceStores(stores []*core.StoreInfo, filters []Filter, opt *config.PersistOptions) []*core.StoreInfo {
	return filterStoresBy(stores, func(s *core.StoreInfo) bool {
		return slice.AllOf(filters, func(i int) bool {
			if !filters[i].Source(opt, s) {
				sourceID := strconv.FormatUint(s.GetID(), 10)
				targetID := ""
				filterCounter.WithLabelValues("filter-source", s.GetAddress(),
					sourceID, filters[i].Scope(), filters[i].Type(), sourceID, targetID).Inc()
				return false
			}
			return true
		})
	})
}
func SelectTargetStores(stores []*core.StoreInfo, filters []Filter, opt *config.PersistOptions) []*core.StoreInfo {
	return filterStoresBy(stores, func(s *core.StoreInfo) bool {
		return slice.AllOf(filters, func(i int) bool {
			filter := filters[i]
			if !filter.Target(opt, s) {
				cfilter, ok := filter.(comparingFilter)
				targetID := strconv.FormatUint(s.GetID(), 10)
				sourceID := ""
				if ok {
					sourceID = strconv.FormatUint(cfilter.GetSourceStoreID(), 10)
				}
				filterCounter.WithLabelValues("filter-target", s.GetAddress(),
					targetID, filters[i].Scope(), filters[i].Type(), sourceID, targetID).Inc()
				return false
			}
			return true
		})
	})
}
func filterStoresBy(stores []*core.StoreInfo, keepPred func(*core.StoreInfo) bool) (selected []*core.StoreInfo) {
	for _, s := range stores {
		if keepPred(s) {
			selected = append(selected, s)
		}
	}
	return
}
type Filter interface {
	Scope() string
	Type() string
	Source(opt *config.PersistOptions, store *core.StoreInfo) bool
	Target(opt *config.PersistOptions, store *core.StoreInfo) bool
}
type comparingFilter interface {
	Filter
	GetSourceStoreID() uint64
}
func Source(opt *config.PersistOptions, store *core.StoreInfo, filters []Filter) bool {
	storeAddress := store.GetAddress()
	storeID := strconv.FormatUint(store.GetID(), 10)
	for _, filter := range filters {
		if !filter.Source(opt, store) {
			sourceID := storeID
			targetID := ""
			filterCounter.WithLabelValues("filter-source", storeAddress,
				sourceID, filter.Scope(), filter.Type(), sourceID, targetID).Inc()
			return false
		}
	}
	return true
}
func Target(opt *config.PersistOptions, store *core.StoreInfo, filters []Filter) bool {
	storeAddress := store.GetAddress()
	storeID := strconv.FormatUint(store.GetID(), 10)
	for _, filter := range filters {
		if !filter.Target(opt, store) {
			cfilter, ok := filter.(comparingFilter)
			targetID := storeID
			sourceID := ""
			if ok {
				sourceID = strconv.FormatUint(cfilter.GetSourceStoreID(), 10)
			}
			filterCounter.WithLabelValues("filter-target", storeAddress,
				targetID, filter.Scope(), filter.Type(), sourceID, targetID).Inc()
			return false
		}
	}
	return true
}
type excludedFilter struct {
	scope   string
	sources map[uint64]struct{}
	targets map[uint64]struct{}
}
func NewExcludedFilter(scope string, sources, targets map[uint64]struct{}) Filter {
	return &excludedFilter{
		scope:   scope,
		sources: sources,
		targets: targets,
	}
}
func (f *excludedFilter) Scope() string {
	return f.scope
}
func (f *excludedFilter) Type() string {
	return "exclude-filter"
}
func (f *excludedFilter) Source(opt *config.PersistOptions, store *core.StoreInfo) bool {
	_, ok := f.sources[store.GetID()]
	return !ok
}
func (f *excludedFilter) Target(opt *config.PersistOptions, store *core.StoreInfo) bool {
	_, ok := f.targets[store.GetID()]
	return !ok
}
type storageThresholdFilter struct{ scope string }
func NewStorageThresholdFilter(scope string) Filter {
	return &storageThresholdFilter{scope: scope}
}
func (f *storageThresholdFilter) Scope() string {
	return f.scope
}
func (f *storageThresholdFilter) Type() string {
	return "storage-threshold-filter"
}
func (f *storageThresholdFilter) Source(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return true
}
func (f *storageThresholdFilter) Target(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return !store.IsLowSpace(opt.GetLowSpaceRatio())
}
type distinctScoreFilter struct {
	scope     string
	labels    []string
	stores    []*core.StoreInfo
	policy    string
	safeScore float64
	srcStore  uint64
}
const (
	locationSafeguard = "safeguard"
	locationImprove   = "improve"
)
func NewLocationSafeguard(scope string, labels []string, stores []*core.StoreInfo, source *core.StoreInfo) Filter {
	return newDistinctScoreFilter(scope, labels, stores, source, locationSafeguard)
}
func NewLocationImprover(scope string, labels []string, stores []*core.StoreInfo, source *core.StoreInfo) Filter {
	return newDistinctScoreFilter(scope, labels, stores, source, locationImprove)
}
func newDistinctScoreFilter(scope string, labels []string, stores []*core.StoreInfo, source *core.StoreInfo, policy string) Filter {
	newStores := make([]*core.StoreInfo, 0, len(stores)-1)
	for _, s := range stores {
		if s.GetID() == source.GetID() {
			continue
		}
		newStores = append(newStores, s)
	}
	return &distinctScoreFilter{
		scope:     scope,
		labels:    labels,
		stores:    newStores,
		safeScore: core.DistinctScore(labels, newStores, source),
		policy:    policy,
		srcStore:  source.GetID(),
	}
}
func (f *distinctScoreFilter) Scope() string {
	return f.scope
}
func (f *distinctScoreFilter) Type() string {
	return "distinct-filter"
}
func (f *distinctScoreFilter) Source(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return true
}
func (f *distinctScoreFilter) Target(opt *config.PersistOptions, store *core.StoreInfo) bool {
	score := core.DistinctScore(f.labels, f.stores, store)
	switch f.policy {
	case locationSafeguard:
		return score >= f.safeScore
	case locationImprove:
		return score > f.safeScore
	default:
		return false
	}
}
func (f *distinctScoreFilter) GetSourceStoreID() uint64 {
	return f.srcStore
}
type StoreStateFilter struct {
	ActionScope string
	TransferLeader bool
	MoveRegion bool
	ScatterRegion bool
	AllowTemporaryStates bool
	Reason string
}
func (f *StoreStateFilter) Scope() string {
	return f.ActionScope
}
func (f *StoreStateFilter) Type() string {
	return fmt.Sprintf("store-state-%s-filter", f.Reason)
}
type conditionFunc func(*config.PersistOptions, *core.StoreInfo) bool
func (f *StoreStateFilter) isTombstone(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "tombstone"
	return store.IsTombstone()
}
func (f *StoreStateFilter) isDown(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "down"
	return store.DownTime() > opt.GetMaxStoreDownTime()
}
func (f *StoreStateFilter) isOffline(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "offline"
	return store.IsOffline()
}
func (f *StoreStateFilter) pauseLeaderTransfer(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "pause-leader"
	return !store.AllowLeaderTransfer()
}
func (f *StoreStateFilter) slowStoreEvicted(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "slow-store"
	return store.EvictedAsSlowStore()
}
func (f *StoreStateFilter) isDisconnected(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "disconnected"
	return !f.AllowTemporaryStates && store.IsDisconnected()
}
func (f *StoreStateFilter) isBusy(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "busy"
	return !f.AllowTemporaryStates && store.IsBusy()
}
func (f *StoreStateFilter) exceedRemoveLimit(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "exceed-remove-limit"
	return !f.AllowTemporaryStates && !store.IsAvailable(storelimit.RemovePeer)
}
func (f *StoreStateFilter) exceedAddLimit(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "exceed-add-limit"
	return !f.AllowTemporaryStates && !store.IsAvailable(storelimit.AddPeer)
}
func (f *StoreStateFilter) tooManySnapshots(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "too-many-snapshot"
	return !f.AllowTemporaryStates && (uint64(store.GetSendingSnapCount()) > opt.GetMaxSnapshotCount() ||
		uint64(store.GetReceivingSnapCount()) > opt.GetMaxSnapshotCount())
}
func (f *StoreStateFilter) tooManyPendingPeers(opt *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "too-many-pending-peer"
	return !f.AllowTemporaryStates &&
		opt.GetMaxPendingPeerCount() > 0 &&
		store.GetPendingPeerCount() > int(opt.GetMaxPendingPeerCount())
}
func (f *StoreStateFilter) hasRejectLeaderProperty(opts *config.PersistOptions, store *core.StoreInfo) bool {
	f.Reason = "reject-leader"
	return opts.CheckLabelProperty(opt.RejectLeader, store.GetLabels())
}
const (
	leaderSource = iota
	regionSource
	leaderTarget
	regionTarget
	scatterRegionTarget
)
func (f *StoreStateFilter) anyConditionMatch(typ int, opt *config.PersistOptions, store *core.StoreInfo) bool {
	var funcs []conditionFunc
	switch typ {
	case leaderSource:
		funcs = []conditionFunc{f.isTombstone, f.isDown, f.pauseLeaderTransfer, f.isDisconnected}
	case regionSource:
		funcs = []conditionFunc{f.isBusy, f.exceedRemoveLimit, f.tooManySnapshots}
	case leaderTarget:
		funcs = []conditionFunc{f.isTombstone, f.isOffline, f.isDown, f.pauseLeaderTransfer,
			f.slowStoreEvicted, f.isDisconnected, f.isBusy, f.hasRejectLeaderProperty}
	case regionTarget:
		funcs = []conditionFunc{f.isTombstone, f.isOffline, f.isDown, f.isDisconnected, f.isBusy,
			f.exceedAddLimit, f.tooManySnapshots, f.tooManyPendingPeers}
	case scatterRegionTarget:
		funcs = []conditionFunc{f.isTombstone, f.isOffline, f.isDown, f.isDisconnected, f.isBusy}
	}
	for _, cf := range funcs {
		if cf(opt, store) {
			return true
		}
	}
	return false
}
func (f *StoreStateFilter) Source(opts *config.PersistOptions, store *core.StoreInfo) bool {
	if f.TransferLeader && f.anyConditionMatch(leaderSource, opts, store) {
		return false
	}
	if f.MoveRegion && f.anyConditionMatch(regionSource, opts, store) {
		return false
	}
	return true
}
func (f *StoreStateFilter) Target(opts *config.PersistOptions, store *core.StoreInfo) bool {
	if f.TransferLeader && f.anyConditionMatch(leaderTarget, opts, store) {
		return false
	}
	if f.MoveRegion && f.ScatterRegion && f.anyConditionMatch(scatterRegionTarget, opts, store) {
		return false
	}
	if f.MoveRegion && !f.ScatterRegion && f.anyConditionMatch(regionTarget, opts, store) {
		return false
	}
	return true
}
type labelConstraintFilter struct {
	scope       string
	constraints []placement.LabelConstraint
}
func NewLabelConstaintFilter(scope string, constraints []placement.LabelConstraint) Filter {
	return labelConstraintFilter{scope: scope, constraints: constraints}
}
func (f labelConstraintFilter) Scope() string {
	return f.scope
}
func (f labelConstraintFilter) Type() string {
	return "label-constraint-filter"
}
func (f labelConstraintFilter) Source(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return placement.MatchLabelConstraints(store, f.constraints)
}
func (f labelConstraintFilter) Target(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return placement.MatchLabelConstraints(store, f.constraints)
}
type ruleFitFilter struct {
	scope    string
	cluster  opt.Cluster
	region   *core.RegionInfo
	oldFit   *placement.RegionFit
	srcStore uint64
}
func newRuleFitFilter(scope string, cluster opt.Cluster, region *core.RegionInfo, oldStoreID uint64) Filter {
	return &ruleFitFilter{
		scope:    scope,
		cluster:  cluster,
		region:   region,
		oldFit:   opt.FitRegion(cluster, region),
		srcStore: oldStoreID,
	}
}
func (f *ruleFitFilter) Scope() string {
	return f.scope
}
func (f *ruleFitFilter) Type() string {
	return "rule-fit-filter"
}
func (f *ruleFitFilter) Source(options *config.PersistOptions, store *core.StoreInfo) bool {
	return true
}
func (f *ruleFitFilter) Target(options *config.PersistOptions, store *core.StoreInfo) bool {
	region := createRegionForRuleFit(f.region.GetStartKey(), f.region.GetEndKey(),
		f.region.GetPeers(), f.region.GetLeader(),
		core.WithReplacePeerStore(f.srcStore, store.GetID()))
	newFit := opt.FitRegion(f.cluster, region)
	return placement.CompareRegionFit(f.oldFit, newFit) <= 0
}
func (f *ruleFitFilter) GetSourceStoreID() uint64 {
	return f.srcStore
}
type ruleLeaderFitFilter struct {
	scope            string
	cluster          opt.Cluster
	region           *core.RegionInfo
	oldFit           *placement.RegionFit
	srcLeaderStoreID uint64
}
func newRuleLeaderFitFilter(scope string, cluster opt.Cluster, region *core.RegionInfo, srcLeaderStoreID uint64) Filter {
	return &ruleLeaderFitFilter{
		scope:            scope,
		cluster:          cluster,
		region:           region,
		oldFit:           opt.FitRegion(cluster, region),
		srcLeaderStoreID: srcLeaderStoreID,
	}
}
func (f *ruleLeaderFitFilter) Scope() string {
	return f.scope
}
func (f *ruleLeaderFitFilter) Type() string {
	return "rule-fit-leader-filter"
}
func (f *ruleLeaderFitFilter) Source(options *config.PersistOptions, store *core.StoreInfo) bool {
	return true
}
func (f *ruleLeaderFitFilter) Target(options *config.PersistOptions, store *core.StoreInfo) bool {
	targetPeer := f.region.GetStorePeer(store.GetID())
	if targetPeer == nil {
		log.Warn("ruleLeaderFitFilter couldn't find peer on target Store", zap.Uint64("target-store", store.GetID()))
		return false
	}
	copyRegion := createRegionForRuleFit(f.region.GetStartKey(), f.region.GetEndKey(),
		f.region.GetPeers(), f.region.GetLeader(),
		core.WithLeader(targetPeer))
	newFit := opt.FitRegion(f.cluster, copyRegion)
	return placement.CompareRegionFit(f.oldFit, newFit) <= 0
}
func (f *ruleLeaderFitFilter) GetSourceStoreID() uint64 {
	return f.srcLeaderStoreID
}
func NewPlacementSafeguard(scope string, cluster opt.Cluster, region *core.RegionInfo, sourceStore *core.StoreInfo) Filter {
	if cluster.GetOpts().IsPlacementRulesEnabled() {
		return newRuleFitFilter(scope, cluster, region, sourceStore.GetID())
	}
	return NewLocationSafeguard(scope, cluster.GetOpts().GetLocationLabels(), cluster.GetRegionStores(region), sourceStore)
}
func NewPlacementLeaderSafeguard(scope string, cluster opt.Cluster, region *core.RegionInfo, sourceStore *core.StoreInfo) Filter {
	if cluster.GetOpts().IsPlacementRulesEnabled() {
		return newRuleLeaderFitFilter(scope, cluster, region, sourceStore.GetID())
	}
	return nil
}
type engineFilter struct {
	scope      string
	constraint placement.LabelConstraint
}
func NewEngineFilter(scope string, allowedEngines ...string) Filter {
	return &engineFilter{
		scope:      scope,
		constraint: placement.LabelConstraint{Key: core.EngineKey, Op: placement.In, Values: allowedEngines},
	}
}
func (f *engineFilter) Scope() string {
	return f.scope
}
func (f *engineFilter) Type() string {
	return "engine-filter"
}
func (f *engineFilter) Source(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return f.constraint.MatchStore(store)
}
func (f *engineFilter) Target(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return f.constraint.MatchStore(store)
}
type ordinaryEngineFilter struct {
	scope      string
	constraint placement.LabelConstraint
}
func NewOrdinaryEngineFilter(scope string) Filter {
	return &ordinaryEngineFilter{
		scope:      scope,
		constraint: placement.LabelConstraint{Key: core.EngineKey, Op: placement.NotIn, Values: allSpecialEngines},
	}
}
func (f *ordinaryEngineFilter) Scope() string {
	return f.scope
}
func (f *ordinaryEngineFilter) Type() string {
	return "ordinary-engine-filter"
}
func (f *ordinaryEngineFilter) Source(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return f.constraint.MatchStore(store)
}
func (f *ordinaryEngineFilter) Target(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return f.constraint.MatchStore(store)
}
type specialUseFilter struct {
	scope      string
	constraint placement.LabelConstraint
}
func NewSpecialUseFilter(scope string, allowUses ...string) Filter {
	var values []string
	for _, v := range allSpecialUses {
		if slice.NoneOf(allowUses, func(i int) bool { return allowUses[i] == v }) {
			values = append(values, v)
		}
	}
	return &specialUseFilter{
		scope:      scope,
		constraint: placement.LabelConstraint{Key: SpecialUseKey, Op: placement.In, Values: values},
	}
}
func (f *specialUseFilter) Scope() string {
	return f.scope
}
func (f *specialUseFilter) Type() string {
	return "special-use-filter"
}
func (f *specialUseFilter) Source(opt *config.PersistOptions, store *core.StoreInfo) bool {
	if store.IsLowSpace(opt.GetLowSpaceRatio()) {
		return true
	}
	return !f.constraint.MatchStore(store)
}
func (f *specialUseFilter) Target(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return !f.constraint.MatchStore(store)
}
const (
	SpecialUseKey = "specialUse"
	SpecialUseHotRegion = "hotRegion"
	SpecialUseReserved = "reserved"
)
var allSpecialUses = []string{SpecialUseHotRegion, SpecialUseReserved}
var allSpecialEngines = []string{core.EngineTiFlash}
type isolationFilter struct {
	scope          string
	locationLabels []string
	constraintSet  [][]string
}
func NewIsolationFilter(scope, isolationLevel string, locationLabels []string, regionStores []*core.StoreInfo) Filter {
	isolationFilter := &isolationFilter{
		scope:          scope,
		locationLabels: locationLabels,
		constraintSet:  make([][]string, 0),
	}
	var isolationLevelIdx int
	for level, label := range locationLabels {
		if label == isolationLevel {
			isolationLevelIdx = level
			break
		}
	}
	for _, regionStore := range regionStores {
		var constraintList []string
		for i := 0; i <= isolationLevelIdx; i++ {
			constraintList = append(constraintList, regionStore.GetLabelValue(locationLabels[i]))
		}
		isolationFilter.constraintSet = append(isolationFilter.constraintSet, constraintList)
	}
	return isolationFilter
}
func (f *isolationFilter) Scope() string {
	return f.scope
}
func (f *isolationFilter) Type() string {
	return "isolation-filter"
}
func (f *isolationFilter) Source(opt *config.PersistOptions, store *core.StoreInfo) bool {
	return true
}
func (f *isolationFilter) Target(opt *config.PersistOptions, store *core.StoreInfo) bool {
	if len(f.constraintSet) <= 0 {
		return true
	}
	for _, constrainList := range f.constraintSet {
		match := true
		for idx, constraint := range constrainList {
			match = store.GetLabelValue(f.locationLabels[idx]) == constraint && match
		}
		if len(constrainList) > 0 && match {
			return false
		}
	}
	return true
}
func createRegionForRuleFit(startKey, endKey []byte,
	peers []*metapb.Peer, leader *metapb.Peer, opts ...core.RegionCreateOption) *core.RegionInfo {
	copyLeader := proto.Clone(leader).(*metapb.Peer)
	copyPeers := make([]*metapb.Peer, 0, len(peers))
	for _, p := range peers {
		peer := &metapb.Peer{
			Id:      p.Id,
			StoreId: p.StoreId,
			Role:    p.Role,
		}
		copyPeers = append(copyPeers, peer)
	}
	cloneRegion := core.NewRegionInfo(&metapb.Region{
		StartKey: startKey,
		EndKey:   endKey,
		Peers:    copyPeers,
	}, copyLeader, opts...)
	return cloneRegion
}
type RegionScoreFilter struct {
	scope string
	score float64
}