Skip to content

Commit bac5602

Browse files
authored
Always use INCRBY for redis rate limits (#7856)
Deprecate the IncrementRateLimits feature flag, and always use the redis INCRBY instruction to update rate limit TATs. Fixes #7855
1 parent 5cdfa3e commit bac5602

File tree

7 files changed

+22
-61
lines changed

7 files changed

+22
-61
lines changed

features/features.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import (
1515
// then call features.Set(parsedConfig) to load the parsed struct into this
1616
// package's global Config.
1717
type Config struct {
18+
// Deprecated flags.
19+
IncrementRateLimits bool
20+
1821
// ServeRenewalInfo exposes the renewalInfo endpoint in the directory and for
1922
// GET requests. WARNING: This feature is a draft and highly unstable.
2023
ServeRenewalInfo bool
@@ -99,11 +102,6 @@ type Config struct {
99102
// and pause issuance for each (account, hostname) pair that repeatedly
100103
// fails validation.
101104
AutomaticallyPauseZombieClients bool
102-
103-
// IncrementRateLimits uses Redis' IncrBy, instead of Set, for rate limit
104-
// accounting. This catches and denies spikes of requests much more
105-
// reliably.
106-
IncrementRateLimits bool
107105
}
108106

109107
var fMu = new(sync.RWMutex)

ratelimits/limiter.go

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/prometheus/client_golang/prometheus"
1515

1616
berrors "github.com/letsencrypt/boulder/errors"
17-
"github.com/letsencrypt/boulder/features"
1817
)
1918

2019
const (
@@ -276,7 +275,6 @@ func (l *Limiter) BatchSpend(ctx context.Context, txns []Transaction) (*Decision
276275
return nil, err
277276
}
278277
batchDecision := allowedDecision
279-
newTATs := make(map[string]time.Time)
280278
newBuckets := make(map[string]time.Time)
281279
incrBuckets := make(map[string]increment)
282280
txnOutcomes := make(map[Transaction]string)
@@ -297,8 +295,6 @@ func (l *Limiter) BatchSpend(ctx context.Context, txns []Transaction) (*Decision
297295

298296
if d.allowed && (tat != d.newTAT) && txn.spend {
299297
// New bucket state should be persisted.
300-
newTATs[txn.bucketKey] = d.newTAT
301-
302298
if bucketExists {
303299
incrBuckets[txn.bucketKey] = increment{
304300
cost: time.Duration(txn.cost * txn.limit.emissionInterval),
@@ -321,25 +317,16 @@ func (l *Limiter) BatchSpend(ctx context.Context, txns []Transaction) (*Decision
321317
}
322318
}
323319

324-
if features.Get().IncrementRateLimits {
325-
if batchDecision.allowed {
326-
if len(newBuckets) > 0 {
327-
err = l.source.BatchSet(ctx, newBuckets)
328-
if err != nil {
329-
return nil, err
330-
}
331-
}
332-
333-
if len(incrBuckets) > 0 {
334-
err = l.source.BatchIncrement(ctx, incrBuckets)
335-
if err != nil {
336-
return nil, err
337-
}
320+
if batchDecision.allowed {
321+
if len(newBuckets) > 0 {
322+
err = l.source.BatchSet(ctx, newBuckets)
323+
if err != nil {
324+
return nil, err
338325
}
339326
}
340-
} else {
341-
if batchDecision.allowed && len(newTATs) > 0 {
342-
err = l.source.BatchSet(ctx, newTATs)
327+
328+
if len(incrBuckets) > 0 {
329+
err = l.source.BatchIncrement(ctx, incrBuckets)
343330
if err != nil {
344331
return nil, err
345332
}
@@ -396,7 +383,6 @@ func (l *Limiter) BatchRefund(ctx context.Context, txns []Transaction) (*Decisio
396383
}
397384

398385
batchDecision := allowedDecision
399-
newTATs := make(map[string]time.Time)
400386
incrBuckets := make(map[string]increment)
401387

402388
for _, txn := range batch {
@@ -414,27 +400,17 @@ func (l *Limiter) BatchRefund(ctx context.Context, txns []Transaction) (*Decisio
414400
batchDecision = stricter(batchDecision, d)
415401
if d.allowed && tat != d.newTAT {
416402
// New bucket state should be persisted.
417-
newTATs[txn.bucketKey] = d.newTAT
418403
incrBuckets[txn.bucketKey] = increment{
419404
cost: time.Duration(-txn.cost * txn.limit.emissionInterval),
420405
ttl: time.Duration(txn.limit.burstOffset),
421406
}
422407
}
423408
}
424409

425-
if features.Get().IncrementRateLimits {
426-
if len(incrBuckets) > 0 {
427-
err = l.source.BatchIncrement(ctx, incrBuckets)
428-
if err != nil {
429-
return nil, err
430-
}
431-
}
432-
} else {
433-
if len(newTATs) > 0 {
434-
err = l.source.BatchSet(ctx, newTATs)
435-
if err != nil {
436-
return nil, err
437-
}
410+
if len(incrBuckets) > 0 {
411+
err = l.source.BatchIncrement(ctx, incrBuckets)
412+
if err != nil {
413+
return nil, err
438414
}
439415
}
440416
return batchDecision, nil

ratelimits/limiter_test.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"math/rand/v2"
66
"net"
7-
"os"
87
"testing"
98
"time"
109

@@ -13,7 +12,6 @@ import (
1312

1413
"github.com/letsencrypt/boulder/config"
1514
berrors "github.com/letsencrypt/boulder/errors"
16-
"github.com/letsencrypt/boulder/features"
1715
"github.com/letsencrypt/boulder/metrics"
1816
"github.com/letsencrypt/boulder/test"
1917
)
@@ -40,19 +38,6 @@ func newTestTransactionBuilder(t *testing.T) *TransactionBuilder {
4038
}
4139

4240
func setup(t *testing.T) (context.Context, map[string]*Limiter, *TransactionBuilder, clock.FakeClock, string) {
43-
// Because all test cases in this file are affected by this feature flag, we
44-
// want to run them all both with and without the feature flag. This way, we
45-
// get one set of runs with and one set without. It's difficult to defer
46-
// features.Reset() from the setup func (these tests are parallel); as long
47-
// as this code doesn't test any other features, we don't need to.
48-
//
49-
// N.b. This is fragile. If a test case does call features.Reset(), it will
50-
// not be testing the intended code path. But we expect to clean this up
51-
// quickly.
52-
if os.Getenv("BOULDER_CONFIG_DIR") == "test/config-next" {
53-
features.Set(features.Config{IncrementRateLimits: true})
54-
}
55-
5641
testCtx := context.Background()
5742
clk := clock.NewFake()
5843

test/config-next/ra.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@
128128
"features": {
129129
"AsyncFinalize": true,
130130
"UseKvLimitsForNewOrder": true,
131-
"AutomaticallyPauseZombieClients": true,
132-
"IncrementRateLimits": true
131+
"AutomaticallyPauseZombieClients": true
133132
},
134133
"ctLogs": {
135134
"stagger": "500ms",

test/config-next/wfe2.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@
128128
"PropagateCancels": true,
129129
"ServeRenewalInfo": true,
130130
"CheckIdentifiersPaused": true,
131-
"UseKvLimitsForNewOrder": true,
132-
"IncrementRateLimits": true
131+
"UseKvLimitsForNewOrder": true
133132
},
134133
"certProfiles": {
135134
"legacy": "The normal profile you know and love",

test/config/ra.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@
104104
}
105105
}
106106
},
107+
"features": {
108+
"IncrementRateLimits": true
109+
},
107110
"ctLogs": {
108111
"stagger": "500ms",
109112
"logListFile": "test/ct-test-srv/log_list.json",

test/config/wfe2.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@
103103
"authorizationLifetimeDays": 30,
104104
"pendingAuthorizationLifetimeDays": 7,
105105
"features": {
106-
"ServeRenewalInfo": true
106+
"ServeRenewalInfo": true,
107+
"IncrementRateLimits": true
107108
}
108109
},
109110
"syslog": {

0 commit comments

Comments
 (0)