Repository navigation
feat: add dry run mode to HTTP limiters - #20
Conversation
|
Hello @MaximeMRF The proposal looks great, but I think we should also think through how the observability can be done when the main goal is to observe the limits and tune the numbers. Right now we are leaning into the |
|
I just added support for emitting and event when the limit is exceeded and the same event can now be extended with a |
Requests are counted on separate keys but never rejected. The limit:exceeded event is emitted with dryRun: true instead of calling limitExceeded, and no rate limit headers are set.
606f3f4 to
8560a10
Compare
|
Rebased on top of #21 as suggested. Dry run no longer touches To avoid a duplicate event when the store rejects, |
🔗 Linked issue
#19
❓ Type of change
📚 Description
This PR adds a dry run mode to
HttpLimiter. In dry run mode, requests are counted as usual, but never rejected: when the limit is exceeded, thelimitExceededcallback is called with the exception that would have been thrown, and the request goes on.It lets developers measure the impact of a limit on real traffic before enforcing it, like the "Count" action of a WAF.
Changes:
dryRun(enabled = true)enables the mode, so it can be driven by an env variable (.dryRun(env.get('LIMITER_DRY_RUN'))).isDryRun()returns it, andtoJSON()includesdryRun: trueonly when enabled, so the existing output is unchanged.throttle()returns the limiter response instead of throwing when the limit is exceeded in dry run mode, after calling thelimitExceededcallback. The callback is called for every request over the limit, which makes it the place to log them.<name>_dry_<key>). Without it, withblockFor(), every key blocked during the dry run would be rejected as soon as.dryRun()is removed.X-RateLimit-LimitandX-RateLimit-Remainingheaders are not set in dry run mode. The dry run should be invisible to clients: some API clients slow down on their own whenX-RateLimit-Remainingreaches 0, which would skew the observation, and bots would learn the limit before it is enforced.Tests cover the config, the requests going through with the callback being called, the separate keys with
blockFor(), and the middleware callingnext()without headers. I also added a test for the headers in the normal mode, which had none.Two questions:
dryRun()the right name?reportOnly()(like the CSP report only mode) would work too.blockFor()reason above.📝 Checklist
I will send the docs PR to v7-docs once this one is accepted.