feat(rate-limit/unstable): add rate limiting module#7063
feat(rate-limit/unstable): add rate limiting module#7063tomas-zijdemans wants to merge 35 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7063 +/- ##
==========================================
+ Coverage 94.83% 94.96% +0.12%
==========================================
Files 617 628 +11
Lines 51674 52447 +773
Branches 9350 9444 +94
==========================================
+ Hits 49007 49806 +799
+ Misses 2121 2098 -23
+ Partials 546 543 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
bartlomieju
left a comment
There was a problem hiding this comment.
This is a well-architected and thoroughly tested package. The layered design (pure algorithms → keyed wrapper → async queue/timer/disposal → public API) is clean, and the algorithm implementations look mathematically correct. Solid work.
A few things to address before merge:
Blockers:
- CI: PR title validation is failing —
rate-limitneeds to be added to the scopes list in.github/workflows/title.ymlin a preceding PR. - Discussion: Is there a tracking issue or RFC for adding a rate-limiting package to
@std? This is a significant addition (~5100 lines) and new packages typically need community buy-in. mod.ts: Ensure it has the@moduleJSDoc tag per repo conventions.
Design/correctness notes:
peek()in keyed algorithms mutates state:peek()callsops.advance(), which rotates segment counters in sliding-window. This means "peeking" isn't truly read-only — it advances time. This is necessary for correct metadata but should be documented explicitly.- Depends on unstable APIs: Imports
RollingCounterfrom@std/data-structures/unstable-rolling-counterand usesDeque. Fine for v0.1.0 but worth noting as a stability risk. - TokenBucket
result()usesthis.computeRetryAfter()rather than calling the method directly — minor style inconsistency with the other algorithm implementations which inline the computation. - Some JSDoc examples use
limiter[Symbol.dispose]()syntax — consider using theusingstatement for consistency with newer examples elsewhere. - Naming inconsistency:
TokenBucketOptionsusestokenLimit+tokensPerPeriod+replenishmentPeriod, whilecreateRateLimiteruseslimit+window+tokensPerCycle. The different naming across the same domain could be confusing. - Floating-point edge cases: TokenBucket uses fractional tokens internally (
Math.floor(state.tokens)for remaining). No tests specifically exercise floating-point boundary behavior (e.g., tokens at 4.999... after refill).
|
Thank you for the review. I pushed a new commit now:
Let me know if you agree/disagree or have any further feedback |
…7046) Sliding expiration: entries can now stay alive as long as they're being accessed, with an optional hard deadline. Useful for sessions or rate-limit windows.
|
Changed to use stable Deque |
|
Preceding PR created as requested: #7176 Issue created as asked for: #7175 @bartlomieju: This should cover the blocking issues you had. I tried to package a MVP in this PR, bu let me know if you want me to break up the PR in smaller chunks/steps. No worries |
|
Sounds good, let's get this one to green |
Casting the setInterval return value with `as number` fails type-checking on newer Deno libs where it resolves to `Timeout` instead of `number` (TS2352). Coerce with `Number(...)` instead, matching the convention used in @std/async/delay. Co-authored-by: Cursor <cursoragent@cursor.com>
All green now :) |
Fixes: #7175
New Rate limit module
This new module offers strategies for controlling how many operations can occur over time. Inspired by .NET’s
System.Threading.RateLimitingThe primary API is
createRateLimiter, a keyed rate limiter for the common case of "allow key X at most N requests per window." It supports fixed-window, sliding-window, token-bucket, and GCRA algorithms.For single-resource limiting, use the primitives:
createTokenBucket,createFixedWindow, andcreateSlidingWindow.The rate limiter supports both in-memory usage and Redis-backed storage for distributed rate limiting across multiple processes or deployments.
Depends on
@std/data-structures:Dequefor async queue management andRollingCounterfor sliding-window segment tracking.Planned future additions