diff --git a/server/schedule/filter/filters.go b/server/schedule/filter/filters.go index bbc532aa365..0aeec5b79d4 100644 --- a/server/schedule/filter/filters.go +++ b/server/schedule/filter/filters.go @@ -16,6 +16,7 @@ package filter import ( "fmt" + "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/log" "github.com/tikv/pd/pkg/slice" "github.com/tikv/pd/server/core" @@ -540,7 +541,9 @@ func (f *ruleFitFilter) Source(opt opt.Options, store *core.StoreInfo) bool { } func (f *ruleFitFilter) Target(opt opt.Options, store *core.StoreInfo) bool { - region := f.region.Clone(core.WithReplacePeerStore(f.oldStore, store.GetID())) + region := createRegionForRuleFit(f.region.GetStartKey(), f.region.GetEndKey(), + f.region.GetPeers(), f.region.GetLeader(), + core.WithReplacePeerStore(f.oldStore, store.GetID())) newFit := f.fitter.FitRegion(region) return placement.CompareRegionFit(f.oldFit, newFit) <= 0 } @@ -583,8 +586,10 @@ func (f *ruleLeaderFitFilter) Target(opt opt.Options, store *core.StoreInfo) boo log.Warn("ruleLeaderFitFilter couldn't find peer on target Store", zap.Uint64("target-store", store.GetID())) return false } - region := f.region.Clone(core.WithLeader(targetPeer)) - newFit := f.fitter.FitRegion(region) + copyRegion := createRegionForRuleFit(f.region.GetStartKey(), f.region.GetEndKey(), + f.region.GetPeers(), f.region.GetLeader(), + core.WithLeader(targetPeer)) + newFit := f.fitter.FitRegion(copyRegion) return placement.CompareRegionFit(f.oldFit, newFit) <= 0 } @@ -712,3 +717,98 @@ const ( var allSpecialUses = []string{SpecialUseHotRegion, SpecialUseReserved} var allSpeicalEngines = []string{EngineTiFlash} +<<<<<<< HEAD +======= + +type isolationFilter struct { + scope string + locationLabels []string + constraintSet [][]string +} + +// NewIsolationFilter creates a filter that filters out stores with isolationLevel +// For example, a region has 3 replicas in z1, z2 and z3 individually. +// With isolationLevel = zone, if the region on z1 is down, we need to filter out z2 and z3 +// because these two zones already have one of the region's replicas on them. +// We need to choose a store on z1 or z4 to place the new replica to meet the isolationLevel explicitly and forcibly. +func NewIsolationFilter(scope, isolationLevel string, locationLabels []string, regionStores []*core.StoreInfo) Filter { + isolationFilter := &isolationFilter{ + scope: scope, + locationLabels: locationLabels, + constraintSet: make([][]string, 0), + } + // Get which idx this isolationLevel at according to locationLabels + var isolationLevelIdx int + for level, label := range locationLabels { + if label == isolationLevel { + isolationLevelIdx = level + break + } + } + // Collect all constraints for given isolationLevel + for _, regionStore := range regionStores { + constraintList := make([]string, 0) + 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 opt.Options, store *core.StoreInfo) bool { + return true +} + +func (f *isolationFilter) Target(opt opt.Options, store *core.StoreInfo) bool { + // No isolation constraint to fit + if len(f.constraintSet) <= 0 { + return true + } + for _, constrainList := range f.constraintSet { + match := true + for idx, constraint := range constrainList { + // Check every constraint in constrainList + match = store.GetLabelValue(f.locationLabels[idx]) == constraint && match + } + if len(constrainList) > 0 && match { + return false + } + } + return true +} + +// createRegionForRuleFit is used to create a clone region with RegionCreateOptions which is only used for +// FitRegion in filter +func createRegionForRuleFit(startKey, endKey []byte, + peers []*metapb.Peer, leader *metapb.Peer, opts ...core.RegionCreateOption) *core.RegionInfo { + copyLeader := &metapb.Peer{ + Id: leader.Id, + StoreId: leader.StoreId, + Role: leader.Role, + } + 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 +} +>>>>>>> a0eba9b... filter: replace region.Clone in filter (#2794) diff --git a/server/schedule/filter/filters_test.go b/server/schedule/filter/filters_test.go index 9a35635ac88..716efccefd4 100644 --- a/server/schedule/filter/filters_test.go +++ b/server/schedule/filter/filters_test.go @@ -80,3 +80,28 @@ func (s *testFiltersSuite) TestRuleFitFilter(c *C) { c.Assert(filter.Target(tc, tc.GetStore(4)), IsFalse) c.Assert(filter.Source(tc, tc.GetStore(4)), IsTrue) } + +func BenchmarkCloneRegionTest(b *testing.B) { + epoch := &metapb.RegionEpoch{ + ConfVer: 1, + Version: 1, + } + region := core.NewRegionInfo( + &metapb.Region{ + Id: 4, + StartKey: []byte("x"), + EndKey: []byte(""), + Peers: []*metapb.Peer{ + {Id: 108, StoreId: 4}, + }, + RegionEpoch: epoch, + }, + &metapb.Peer{Id: 108, StoreId: 4}, + core.SetApproximateSize(50), + core.SetApproximateKeys(20), + ) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = createRegionForRuleFit(region.GetStartKey(), region.GetEndKey(), region.GetPeers(), region.GetLeader()) + } +}