Skip to content

Commit 0301106

Browse files
ratelimits: Fix potential data race in the limit registry (#8628)
1 parent 4027aa1 commit 0301106

2 files changed

Lines changed: 23 additions & 3 deletions

File tree

ratelimits/limit.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"sort"
1111
"strconv"
1212
"strings"
13+
"sync"
1314
"time"
1415

1516
"github.com/prometheus/client_golang/prometheus"
@@ -313,11 +314,17 @@ func parseDefaultLimits(newDefaultLimits LimitConfigs) (Limits, error) {
313314
type OverridesRefresher func(context.Context, prometheus.Gauge, blog.Logger) (Limits, error)
314315

315316
type limitRegistry struct {
317+
sync.RWMutex
318+
316319
// defaults stores default limits by 'name'.
317320
defaults Limits
318321

319322
// overrides stores override limits by 'name:id'.
320-
overrides Limits
323+
overrides Limits
324+
325+
// overridesLoaded is true if at least one loadOverrides attempt has
326+
// completed successfully. Callers should check this using the Ready()
327+
// method.
321328
overridesLoaded bool
322329

323330
// refreshOverrides is a function to refresh override limits.
@@ -341,6 +348,8 @@ func (l *limitRegistry) getLimit(name Name, bucketKey string) (*Limit, error) {
341348
return nil, fmt.Errorf("specified name enum %q, is invalid", name)
342349
}
343350
if bucketKey != "" {
351+
l.RLock()
352+
defer l.RUnlock()
344353
// Check for override.
345354
ol, ok := l.overrides[bucketKey]
346355
if ok {
@@ -360,11 +369,14 @@ func (l *limitRegistry) loadOverrides(ctx context.Context) error {
360369
if err != nil {
361370
return err
362371
}
372+
373+
l.Lock()
374+
defer l.Unlock()
363375
l.overridesLoaded = true
364376

365377
if len(newOverrides) < 1 {
366-
l.logger.Warning("loading overrides: no valid overrides")
367378
// If it's an empty set, don't replace any current overrides.
379+
l.logger.Warning("loading overrides: no valid overrides")
368380
return nil
369381
}
370382

@@ -383,6 +395,14 @@ func (l *limitRegistry) loadOverrides(ctx context.Context) error {
383395
return nil
384396
}
385397

398+
// Ready reports whether at least one override load attempt has completed
399+
// successfully.
400+
func (l *limitRegistry) Ready() bool {
401+
l.RLock()
402+
defer l.RUnlock()
403+
return l.overridesLoaded
404+
}
405+
386406
// loadOverridesWithRetry tries to loadOverrides, retrying at least every 30
387407
// seconds upon failure.
388408
func (l *limitRegistry) loadOverridesWithRetry(ctx context.Context) error {

ratelimits/transaction.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ type TransactionBuilder struct {
187187
}
188188

189189
func (builder *TransactionBuilder) Ready() bool {
190-
return builder.limitRegistry.overridesLoaded
190+
return builder.limitRegistry.Ready()
191191
}
192192

193193
// GetOverridesFunc is used to pass in the sa.GetEnabledRateLimitOverrides

0 commit comments

Comments
 (0)