Skip to content

Commit 70a6f5b

Browse files
ti-chi-botlhy1024
andauthored
schedule/labeler: Optimize locking to fix high-concurrency goroutine surge (#9857) (#9897)
close #9854 Signed-off-by: lhy1024 <admin@liudos.us> Co-authored-by: lhy1024 <liuhanyang@pingcap.com>
1 parent 48eca84 commit 70a6f5b

File tree

2 files changed

+85
-26
lines changed

2 files changed

+85
-26
lines changed

pkg/schedule/labeler/labeler.go

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -156,81 +156,139 @@ func (l *RegionLabeler) GetSplitKeys(start, end []byte) [][]byte {
156156
return l.rangeList.GetSplitKeys(start, end)
157157
}
158158

159+
func filterExpiredLabels(rule *LabelRule, now time.Time) *LabelRule {
160+
if rule == nil {
161+
return nil
162+
}
163+
164+
hasExpired := false
165+
for i := range rule.Labels {
166+
if rule.Labels[i].expireBefore(now) {
167+
hasExpired = true
168+
break
169+
}
170+
}
171+
if !hasExpired {
172+
return rule
173+
}
174+
175+
labels := make([]RegionLabel, 0, len(rule.Labels))
176+
for i := range rule.Labels {
177+
if !rule.Labels[i].expireBefore(now) {
178+
labels = append(labels, rule.Labels[i])
179+
}
180+
}
181+
if len(labels) == 0 {
182+
return nil
183+
}
184+
return &LabelRule{
185+
ID: rule.ID,
186+
Index: rule.Index,
187+
RuleType: rule.RuleType,
188+
Data: rule.Data,
189+
Labels: labels,
190+
}
191+
}
192+
159193
// GetAllLabelRules returns all the rules.
160194
func (l *RegionLabeler) GetAllLabelRules() []*LabelRule {
161-
l.checkAndClearExpiredLabels()
162195
l.RLock()
163196
defer l.RUnlock()
197+
198+
now := time.Now()
164199
rules := make([]*LabelRule, 0, len(l.labelRules))
165200
for _, rule := range l.labelRules {
166-
rules = append(rules, rule)
201+
if filteredRule := filterExpiredLabels(rule, now); filteredRule != nil {
202+
rules = append(rules, filteredRule)
203+
}
167204
}
168205
return rules
169206
}
170207

171208
// GetLabelRules returns the rules that match the given ids.
172209
func (l *RegionLabeler) GetLabelRules(ids []string) ([]*LabelRule, error) {
210+
l.RLock()
211+
defer l.RUnlock()
212+
173213
now := time.Now()
174214
rules := make([]*LabelRule, 0, len(ids))
175215
for _, id := range ids {
176-
if rule := l.getAndCheckRule(id, now); rule != nil {
177-
rules = append(rules, rule)
216+
if rule, ok := l.labelRules[id]; ok {
217+
if filteredRule := filterExpiredLabels(rule, now); filteredRule != nil {
218+
rules = append(rules, filteredRule)
219+
}
178220
}
179221
}
180222
return rules, nil
181223
}
182224

183225
// GetLabelRule returns the Rule with the same ID.
184226
func (l *RegionLabeler) GetLabelRule(id string) *LabelRule {
185-
return l.getAndCheckRule(id, time.Now())
186-
}
187-
188-
func (l *RegionLabeler) getAndCheckRule(id string, now time.Time) *LabelRule {
189-
l.Lock()
190-
defer l.Unlock()
227+
l.RLock()
228+
defer l.RUnlock()
191229
rule, ok := l.labelRules[id]
192230
if !ok {
193231
return nil
194232
}
195-
if !rule.checkAndRemoveExpireLabels(now) {
196-
return rule
197-
}
198-
if len(rule.Labels) == 0 {
199-
l.storage.DeleteRegionRule(id)
200-
delete(l.labelRules, id)
201-
return nil
202-
}
203-
l.storage.SaveRegionRule(id, rule)
204-
return rule
233+
return filterExpiredLabels(rule, time.Now())
205234
}
206235

207236
// SetLabelRule inserts or updates a LabelRule.
208237
func (l *RegionLabeler) SetLabelRule(rule *LabelRule) error {
209238
if err := rule.checkAndAdjust(); err != nil {
210239
return err
211240
}
241+
if err := l.storage.SaveRegionRule(rule.ID, rule); err != nil {
242+
return err
243+
}
244+
245+
// only Lock for in-memory update
212246
l.Lock()
213247
defer l.Unlock()
248+
l.labelRules[rule.ID] = rule
249+
l.buildRangeList()
250+
return nil
251+
}
252+
253+
// SetLabelRuleLocked inserts or updates a LabelRule but not buildRangeList.
254+
// It updates the in-memory states and storage at the same time.
255+
// It should be used in watcher.
256+
func (l *RegionLabeler) SetLabelRuleLocked(rule *LabelRule) error {
257+
if err := rule.checkAndAdjust(); err != nil {
258+
return err
259+
}
214260
if err := l.storage.SaveRegionRule(rule.ID, rule); err != nil {
215261
return err
216262
}
217263
l.labelRules[rule.ID] = rule
218-
l.buildRangeList()
219264
return nil
220265
}
221266

222267
// DeleteLabelRule removes a LabelRule.
223268
func (l *RegionLabeler) DeleteLabelRule(id string) error {
269+
if err := l.storage.DeleteRegionRule(id); err != nil {
270+
return err
271+
}
272+
273+
// only Lock for in-memory update
224274
l.Lock()
225275
defer l.Unlock()
226276
if _, ok := l.labelRules[id]; !ok {
227-
return errs.ErrRegionRuleNotFound.FastGenByArgs(id)
277+
return nil
228278
}
279+
delete(l.labelRules, id)
280+
l.buildRangeList()
281+
return nil
282+
}
283+
284+
// DeleteLabelRuleLocked removes a LabelRule but not buildRangeList.
285+
// It updates the in-memory states and storage at the same time.
286+
// It should be used in watcher.
287+
func (l *RegionLabeler) DeleteLabelRuleLocked(id string) error {
229288
if err := l.storage.DeleteRegionRule(id); err != nil {
230289
return err
231290
}
232291
delete(l.labelRules, id)
233-
l.buildRangeList()
234292
return nil
235293
}
236294

pkg/schedule/labeler/labeler_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,18 +312,19 @@ func TestLabelerRuleTTL(t *testing.T) {
312312

313313
re.NoError(failpoint.Disable("github.com/tikv/pd/pkg/schedule/labeler/regionLabelExpireSub1Minute"))
314314
// rule2 should be exist since `GetRegionLabels` won't clear it physically.
315-
checkRuleInMemoryAndStoage(re, labeler, "rule2", true)
315+
checkRuleInMemoryAndStorage(re, labeler, "rule2", true)
316316
re.Nil(labeler.GetLabelRule("rule2"))
317317
// rule2 should be physically clear.
318-
checkRuleInMemoryAndStoage(re, labeler, "rule2", false)
318+
labeler.checkAndClearExpiredLabels()
319+
checkRuleInMemoryAndStorage(re, labeler, "rule2", false)
319320

320321
re.Equal("", labeler.GetRegionLabel(region, "k2"))
321322

322323
re.NotNil(labeler.GetLabelRule("rule3"))
323324
re.NotNil(labeler.GetLabelRule("rule1"))
324325
}
325326

326-
func checkRuleInMemoryAndStoage(re *require.Assertions, labeler *RegionLabeler, ruleID string, exist bool) {
327+
func checkRuleInMemoryAndStorage(re *require.Assertions, labeler *RegionLabeler, ruleID string, exist bool) {
327328
re.Equal(exist, labeler.labelRules[ruleID] != nil)
328329
existInStorage := false
329330
labeler.storage.LoadRegionRules(func(k, v string) {

0 commit comments

Comments
 (0)