-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrate-limiter.js
More file actions
73 lines (64 loc) · 1.94 KB
/
Copy pathrate-limiter.js
File metadata and controls
73 lines (64 loc) · 1.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// ==========================================
// Provider Rate Limiter — token bucket with queuing
// ==========================================
// Prevents hitting API rate limits by queuing requests
// and releasing them at a controlled rate.
const buckets = new Map(); // provider -> { tokens, lastRefill, queue }
const DEFAULT_RATE = {
tokensPerMinute: 60, // max requests per minute
burstSize: 10, // max burst before queuing
};
// Per-provider rate configs (override via env)
function getRate(provider) {
const envRate = process.env[`RATE_LIMIT_${provider.toUpperCase().replace(/-/g, '_')}_RPM`];
if (envRate) {
const rpm = parseInt(envRate);
return { tokensPerMinute: rpm, burstSize: Math.ceil(rpm / 6) };
}
return DEFAULT_RATE;
}
function getBucket(provider) {
if (!buckets.has(provider)) {
const rate = getRate(provider);
buckets.set(provider, {
tokens: rate.burstSize,
lastRefill: Date.now(),
rate,
queue: [],
});
}
return buckets.get(provider);
}
function refillTokens(bucket) {
const now = Date.now();
const elapsed = now - bucket.lastRefill;
const tokensToAdd = (elapsed / 60000) * bucket.rate.tokensPerMinute;
bucket.tokens = Math.min(bucket.rate.burstSize, bucket.tokens + tokensToAdd);
bucket.lastRefill = now;
}
/**
* Acquire a token for the given provider. Resolves when a token is available.
*/
export function acquireToken(provider) {
const bucket = getBucket(provider);
refillTokens(bucket);
if (bucket.tokens >= 1) {
bucket.tokens -= 1;
return Promise.resolve();
}
// Queue the request
return new Promise(resolve => {
bucket.queue.push(resolve);
});
}
// Drain queues periodically
setInterval(() => {
for (const [, bucket] of buckets) {
refillTokens(bucket);
while (bucket.queue.length > 0 && bucket.tokens >= 1) {
bucket.tokens -= 1;
const resolve = bucket.queue.shift();
resolve();
}
}
}, 1000).unref();