From 7c2ea153c5b194213783f5e4db910a2e3dc93c47 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 29 Jun 2026 18:33:03 -0400 Subject: [PATCH] Use regional cache for rate limiting --- server/private/lib/rateLimit.ts | 34 ++++++++++++++-------------- server/private/lib/redis.ts | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/server/private/lib/rateLimit.ts b/server/private/lib/rateLimit.ts index a8cf3c01c..32aa6419c 100644 --- a/server/private/lib/rateLimit.ts +++ b/server/private/lib/rateLimit.ts @@ -12,7 +12,7 @@ */ import logger from "@server/logger"; -import redisManager from "#private/lib/redis"; +import { regionalRedisManager as redisManager } from "#private/lib/redis"; import { build } from "@server/build"; // Rate limiting configuration @@ -152,10 +152,9 @@ export class RateLimitService { ); // Set TTL using the client directly - this prevents the key from persisting forever - if (redisManager.getClient()) { - await redisManager - .getClient() - .expire(globalKey, RATE_LIMIT_WINDOW + 10); + const writeClient = redisManager.getClient(); + if (writeClient) { + await writeClient.expire(globalKey, RATE_LIMIT_WINDOW + 10); } // Update tracking @@ -204,10 +203,12 @@ export class RateLimitService { ); // Set TTL using the client directly - this prevents the key from persisting forever - if (redisManager.getClient()) { - await redisManager - .getClient() - .expire(messageTypeKey, RATE_LIMIT_WINDOW + 10); + const writeClient = redisManager.getClient(); + if (writeClient) { + await writeClient.expire( + messageTypeKey, + RATE_LIMIT_WINDOW + 10 + ); } // Update tracking @@ -487,16 +488,13 @@ export class RateLimitService { await redisManager.del(globalKey); // Get all message type keys for this client and delete them - const client = redisManager.getClient(); - if (client) { - const messageTypeKeys = await client.keys( - `ratelimit:${clientId}:*` + const messageTypeKeys = await redisManager.keys( + `ratelimit:${clientId}:*` + ); + if (messageTypeKeys.length > 0) { + await Promise.all( + messageTypeKeys.map((key) => redisManager.del(key)) ); - if (messageTypeKeys.length > 0) { - await Promise.all( - messageTypeKeys.map((key) => redisManager.del(key)) - ); - } } } } diff --git a/server/private/lib/redis.ts b/server/private/lib/redis.ts index 7c3d67836..bda2e6204 100644 --- a/server/private/lib/redis.ts +++ b/server/private/lib/redis.ts @@ -1000,6 +1000,45 @@ class RegionalRedisManager { } } + public getClient(): Redis | null { + return this.writeClient; + } + + public async hget(key: string, field: string): Promise { + if (!this.isRedisEnabled() || !this.readClient) return null; + try { + return await this.readClient.hget(key, field); + } catch (error) { + logger.error("Regional Redis HGET error:", error); + return null; + } + } + + public async hset( + key: string, + field: string, + value: string + ): Promise { + if (!this.isRedisEnabled() || !this.writeClient) return false; + try { + await this.writeClient.hset(key, field, value); + return true; + } catch (error) { + logger.error("Regional Redis HSET error:", error); + return false; + } + } + + public async hgetall(key: string): Promise> { + if (!this.isRedisEnabled() || !this.readClient) return {}; + try { + return await this.readClient.hgetall(key); + } catch (error) { + logger.error("Regional Redis HGETALL error:", error); + return {}; + } + } + public async disconnect(): Promise { try { if (this.writeClient) {