@@ -53,6 +53,19 @@ Picture a bucket that holds tokens:
5353 request limited (bucket empty)
5454```
5555
56+ The same idea as a flow diagram:
57+
58+ ``` mermaid
59+ flowchart TD
60+ R["Refill: +r tokens / second"] -->|"drip"| B
61+ B{"Bucket<br/>capacity = burst b"}
62+ B -->|"overflow above b"| X["discarded"]
63+ Req(["Incoming request<br/>needs 1 token"]) --> Q{"tokens ≥ 1 ?"}
64+ Q -->|"yes"| Take["consume 1 token"] --> Allowed(["✅ allowed"])
65+ Q -->|"no"| Limited(["⛔ limited — reject or wait"])
66+ B -.->|"current level"| Q
67+ ```
68+
5669Rules:
5770
58711 . Tokens are added to the bucket at a steady ** rate** ` r ` (tokens per second).
@@ -208,6 +221,24 @@ full bucket. Choose `deleteAfter` comfortably longer than the window over which
208221you want the limit to hold (e.g. minutes, not milliseconds) so a client cannot
209222reset its own bucket by pausing briefly.
210223
224+ The lifecycle of a single key:
225+
226+ ``` mermaid
227+ stateDiagram-v2
228+ [*] --> Absent
229+ Absent --> Active: GetOrAdd(key)<br/>newLimiter() builds a fresh bucket
230+ Active --> Active: GetOrAdd(key)<br/>refreshes idle timer
231+ Active --> Idle: no access for deleteAfter
232+ Idle --> Active: GetOrAdd(key)<br/>before the sweep runs
233+ Idle --> Absent: sweepLoop evicts<br/>(state discarded)
234+ Active --> Absent: Remove(key)
235+ Absent --> [*]
236+ ```
237+
238+ The sweep is not instantaneous, so a key idle past ` deleteAfter ` lingers until
239+ the next tick — worst case ~ 1.5·` deleteAfter ` after its last use with the
240+ default interval.
241+
211242## HTTP response headers
212243
213244Well-behaved HTTP clients can self-throttle if you tell them the state of their
@@ -227,6 +258,34 @@ the example also emits the legacy `X-RateLimit-*` variants for older clients.
227258[ RFC 9110 §10.2.3] ( https://www.rfc-editor.org/rfc/rfc9110#section-10.2.3 ) and is
228259computed from the reservation delay so it is accurate rather than a guess.
229260
261+ End to end, a request through the middleware:
262+
263+ ``` mermaid
264+ sequenceDiagram
265+ autonumber
266+ participant Client
267+ participant MW as Middleware
268+ participant BL as BucketLimiter
269+ participant Lim as Limiter (per-IP bucket)
270+ participant H as Handler
271+
272+ Client->>MW: HTTP request
273+ MW->>MW: extract client IP<br/>(net.SplitHostPort)
274+ MW->>BL: GetOrAdd(ip)
275+ BL-->>MW: Limiter
276+ MW->>Lim: ReserveN(now, 1)
277+ alt token available now (delay == 0)
278+ Lim-->>MW: reservation, DelayFrom(now) = 0
279+ MW->>H: serve
280+ H-->>MW: response
281+ MW-->>Client: 200 OK<br/>RateLimit-Limit / Remaining / Reset
282+ else bucket empty (delay > 0)
283+ Lim-->>MW: reservation, DelayFrom(now) = d
284+ MW->>Lim: reservation.Cancel()<br/>(return the token)
285+ MW-->>Client: 429 Too Many Requests<br/>Retry-After: ⌈d⌉
286+ end
287+ ```
288+
230289## Comparison with other algorithms
231290
232291| Algorithm | Bursts | Memory/key | Notes |
0 commit comments