You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A TypeScript/JavaScript implementation of an **AIMD (Additive Increase Multiplicative Decrease)** rate limiting token bucket with adaptive rate adjustment. This library is ideal for clients in distributed systems that need to discover and adapt to unknown server-side rate limits dynamically.
3
+
A TypeScript/JavaScript implementation of an **AIMD (Additive Increase Multiplicative Decrease)** rate limiting token bucket with adaptive rate adjustment. This library is ideal for clients in distributed systems that need to discover and adapt to unknown remote system limits dynamically.
4
4
5
5
You can create a bucket with some configured defaults and boundaries, and then ask for tokens from it. You report if each token was successful in doing a unit of work, or if it encountered an error or a server side rate limit. The bucket will then start brokering tokens faster or slower depending on the outcomes you report. The bucket adjusts the rate limit using the smae simple adaptive limiting algorithm used in TCP: AIMD.
6
6
7
+
## Example uses cases
8
+
9
+
- throttle your outgoing requests to a system that starts breaking under load to not break it
10
+
- throttle your outgoing requests to a rate limited API from 3 different processes all competing for that rate limit
11
+
7
12
## Installation
8
13
9
14
```bash
@@ -19,9 +24,12 @@ pnpm add aimd-bucket
19
24
```typescript
20
25
import { AIMDBucket } from"aimd-bucket";
21
26
22
-
// Create a bucket with default settings
27
+
// Create a bucket with default settings (starts with unlimited rate)
23
28
const bucket =newAIMDBucket();
24
29
30
+
// Or create a bucket with a conservative initial rate
The AIMD Bucket automatically emits OpenTelemetry spans when tokens are not immediately available and require waiting. This provides valuable observability into rate limiting behavior without any additional configuration.
149
-
150
-
#### Emitted Spans
151
-
152
-
**Span Name**: `token-bucket.wait`
153
-
154
-
**When Emitted**: Only when `acquire()` cannot immediately provide a token and the request must wait for capacity to become available.
155
-
156
-
**Attributes**:
157
-
158
-
-`token_bucket.current_rate` (number): Current rate limit in tokens per second
159
-
-`token_bucket.available_tokens` (number): Number of tokens currently available
160
-
-`token_bucket.pending_requests` (number): Number of requests currently waiting for tokens
161
-
162
-
**Example Usage**:
163
-
164
-
```typescript
165
-
import { trace } from"@opentelemetry/api";
166
-
167
-
// The span is automatically created when waiting is required
168
-
const token =awaitbucket.acquire(); // May create a span if tokens aren't immediately available
169
-
170
-
// You can access the current span context if needed
171
-
const currentSpan =trace.getActiveSpan();
172
-
if (currentSpan) {
173
-
console.log("Current span:", currentSpan.name);
174
-
}
175
-
```
176
-
177
-
**Note**: No spans are emitted for immediate token acquisition when capacity is available. Spans are only created when there's an actual wait period, providing focused observability on rate limiting bottlenecks.
178
-
179
-
### Graceful Shutdown
180
-
181
-
```typescript
182
-
// Shutdown the bucket gracefully
183
-
awaitbucket.shutdown();
184
-
185
-
// All pending acquire() calls will be rejected
186
-
try {
187
-
awaitbucket.acquire(); // This will throw an error
188
-
} catch (error) {
189
-
console.log("Bucket is shut down");
190
-
}
191
-
```
192
-
193
84
## Token Lifecycle
194
85
195
86
Each token must be completed exactly once with one of these methods:
@@ -199,16 +90,18 @@ Each token must be completed exactly once with one of these methods:
199
90
-`token.rateLimited()` - Request was rate limited (429 status)
200
91
-`token.timeout()` - Request timed out
201
92
93
+
Successful responses will allow the rate limit to increase, and failed responses will force the rate limit to decrease.
94
+
202
95
### Token States
203
96
204
97
```typescript
205
98
const token =awaitbucket.acquire();
206
99
207
-
console.log(token.isCompleted()); // false
208
-
console.log(token.isExpired()); // false
100
+
console.log(token.isCompleted); // false
101
+
console.log(token.isExpired); // false
209
102
210
103
token.success();
211
-
console.log(token.isCompleted()); // true
104
+
console.log(token.isCompleted); // true
212
105
213
106
// Cannot complete the same token twice
214
107
token.success(); // Throws error: "Token has already been completed"
0 commit comments