Skip to content

Commit 7cdf546

Browse files
committed
Refactor rate limiting to use separate read/write Redis operations
1 parent 1bfcd37 commit 7cdf546

File tree

1 file changed

+26
-6
lines changed
  • packages/service-utils/src/core/rateLimit

1 file changed

+26
-6
lines changed

packages/service-utils/src/core/rateLimit/index.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ const RATE_LIMIT_WINDOW_SECONDS = 10;
55

66
// Redis interface compatible with ioredis (Node) and upstash (Cloudflare Workers).
77
type IRedis = {
8-
incr: (key: string) => Promise<number>;
9-
expire: (key: string, ttlSeconds: number) => Promise<0 | 1>;
8+
get: (key: string) => Promise<string | null>;
9+
set(
10+
key: string,
11+
value: string | number,
12+
secondsToken?: "EX" | "XX",
13+
seconds?: number | string,
14+
): Promise<"OK">;
1015
};
1116

1217
export async function rateLimit(args: {
@@ -20,6 +25,7 @@ export async function rateLimit(args: {
2025
* @default 1.0
2126
*/
2227
sampleRate?: number;
28+
logger?: typeof console;
2329
}): Promise<RateLimitResult> {
2430
const { team, limitPerSecond, serviceConfig, redis, sampleRate = 1.0 } = args;
2531

@@ -49,11 +55,25 @@ export async function rateLimit(args: {
4955
RATE_LIMIT_WINDOW_SECONDS;
5056
const key = `rate-limit:${serviceScope}:${team.id}:${timestampWindow}`;
5157

52-
// Increment and get the current request count in this window.
53-
const requestCount = await redis.incr(key);
58+
// first read the request count from redis
59+
const currentRequestCountFromRedis = Number((await redis.get(key)) || "0");
60+
61+
const requestCount = currentRequestCountFromRedis + 1;
62+
63+
// we are setting the request count, however we are not waiting on this to be complete
5464
if (requestCount === 1) {
55-
// For the first increment, set an expiration to clean up this key.
56-
await redis.expire(key, RATE_LIMIT_WINDOW_SECONDS);
65+
// For the first increment, set an expiration to clean up this key (EX).
66+
redis
67+
.set(key, requestCount, "EX", RATE_LIMIT_WINDOW_SECONDS)
68+
.catch((err) => {
69+
console.error("failed to set request count", err);
70+
});
71+
} else {
72+
// For all other increments, just increment the request count.
73+
// only set it if it already exists (XX)
74+
redis.set(key, requestCount, "XX").catch((err) => {
75+
console.error("failed to increment request count", err);
76+
});
5777
}
5878

5979
// Get the limit for this window accounting for the sample rate.

0 commit comments

Comments
 (0)