From fb7d1d724f3c0a3f8e7e8b63c8797e52b1dda594 Mon Sep 17 00:00:00 2001 From: vipul674 Date: Sun, 21 Jun 2026 13:30:46 +0530 Subject: [PATCH] docs(rate-limit): clarify KV-backed persistence vs in-memory fallback Improve documentation for RateLimiter and RefreshRateLimiter to clearly explain the dual-mode behavior: KV-backed persistence when configured, in-memory fallback when not. Add setup instructions reference for Upstash Redis/Vercel KV. --- lib/rate-limit.ts | 14 ++++++++++---- services/github/refresh-rate-limiter.ts | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/rate-limit.ts b/lib/rate-limit.ts index 1eceea21c..a57714c50 100644 --- a/lib/rate-limit.ts +++ b/lib/rate-limit.ts @@ -8,11 +8,17 @@ interface RateLimitResult { } /** - * In-memory rate limiter to prevent basic DoS/spam (Denial of Wallet). + * Rate limiter to prevent basic DoS/spam (Denial of Wallet). * - * Note: In a serverless environment, this resets per cold-start/instance, - * but it is highly effective at stopping aggressive single-instance spikes. - * For multi-instance strict syncing, a Redis store (Vercel KV/Upstash) should be used. + * When Upstash Redis / Vercel KV is configured (KV_REST_API_URL + KV_REST_API_TOKEN + * environment variables), rate limit state is persisted across restarts and shared + * across all serverless instances via atomic INCR + EXPIRE operations. + * + * Falls back to an in-memory TTL cache when KV is not configured. In this mode, + * state resets on cold start / server restart, but it is highly effective at + * stopping aggressive single-instance spikes during normal operation. + * + * @see https://upstash.com/docs/rate-limiting/quickstart for KV setup instructions. */ export class RateLimiter { private cache: DistributedCache<{ count: number; resetAt: number }>; diff --git a/services/github/refresh-rate-limiter.ts b/services/github/refresh-rate-limiter.ts index 6d3de1ab2..93ed9c33d 100644 --- a/services/github/refresh-rate-limiter.ts +++ b/services/github/refresh-rate-limiter.ts @@ -5,6 +5,20 @@ interface RefreshLimitRecord { windowStart: number; } +/** + * In-memory rate limiter for manual cache refresh operations. + * + * Limits how frequently a single IP can trigger a manual data refresh. + * Uses a TTL-based in-memory cache that expires entries automatically. + * + * ⚠️ Limitation: State is per-process only. In serverless deployments + * (Vercel, AWS Lambda), each cold start resets the limiter. This is + * acceptable for abuse prevention on a single instance, but not suitable + * for strict cross-instance rate limiting. + * + * For production deployments requiring persistent state, consider using + * Upstash Redis or Vercel KV by importing from '@/lib/rate-limit' instead. + */ export class RefreshRateLimiter { private static instance: RefreshRateLimiter;