From 71348958fdba2aae95b1f8520e4146738a5b3c0f Mon Sep 17 00:00:00 2001 From: Kunal Keshari Pattanaik Date: Thu, 11 Jun 2026 14:33:31 +0530 Subject: [PATCH] perf: prune rate limiter clients in background --- beamsync/server.go | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/beamsync/server.go b/beamsync/server.go index a2f3559..7380fee 100644 --- a/beamsync/server.go +++ b/beamsync/server.go @@ -30,6 +30,8 @@ type EventCallback func(eventName string, data string) //go:embed ui/*.html ui/*.png var uiFS embed.FS +const rateLimitPruneInterval = 2 * time.Minute + // serverState holds per-instance connection tracking (no more package-level globals). type serverState struct { mu sync.Mutex @@ -98,13 +100,30 @@ type clientRateLimiter struct { } func newClientRateLimiter(limit int, window time.Duration) *clientRateLimiter { - return &clientRateLimiter{ + limiter := &clientRateLimiter{ limit: limit, window: window, maxClients: 4096, clients: make(map[string]*rateLimitState), now: time.Now, } + limiter.startBackgroundPruner() + return limiter +} + +func (l *clientRateLimiter) startBackgroundPruner() { + if l.limit <= 0 || l.window <= 0 { + return + } + + go func() { + ticker := time.NewTicker(rateLimitPruneInterval) + defer ticker.Stop() + + for range ticker.C { + l.prune(l.now()) + } + }() } func (l *clientRateLimiter) allow(client string) rateLimitDecision { @@ -117,8 +136,6 @@ func (l *clientRateLimiter) allow(client string) rateLimitDecision { l.mu.Lock() defer l.mu.Unlock() - l.prune(now) - state, ok := l.clients[client] if !ok || now.Sub(state.windowStart) >= l.window { if !ok { @@ -159,6 +176,9 @@ func (l *clientRateLimiter) allow(client string) rateLimitDecision { } func (l *clientRateLimiter) prune(now time.Time) { + l.mu.Lock() + defer l.mu.Unlock() + for client, state := range l.clients { if now.Sub(state.lastSeen) > 2*l.window { delete(l.clients, client)