From ba8054dc7f423ece33a2686c944b8febc7377c61 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Bajpai Date: Sun, 21 Jun 2026 00:59:57 +0530 Subject: [PATCH 1/2] prune rate limiter in background --- beamsync/rate_limiter_test.go | 26 +++++++++++++++++++ beamsync/server.go | 48 ++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/beamsync/rate_limiter_test.go b/beamsync/rate_limiter_test.go index 4664a0a..7d69913 100644 --- a/beamsync/rate_limiter_test.go +++ b/beamsync/rate_limiter_test.go @@ -71,6 +71,32 @@ func TestClientRateLimiterEvictsOldestClientWhenFull(t *testing.T) { } } +func TestClientRateLimiterPrunesExpiredClients(t *testing.T) { + now := time.Date(2026, 5, 28, 0, 0, 0, 0, time.UTC) + limiter := newClientRateLimiter(2, time.Minute) + limiter.now = func() time.Time { return now } + + limiter.clients["10.0.0.1"] = &rateLimitState{ + windowStart: now.Add(-3 * time.Minute), + count: 1, + lastSeen: now.Add(-3 * time.Minute), + } + limiter.clients["10.0.0.2"] = &rateLimitState{ + windowStart: now.Add(-30 * time.Second), + count: 1, + lastSeen: now.Add(-30 * time.Second), + } + + limiter.prune(now) + + if _, ok := limiter.clients["10.0.0.1"]; ok { + t.Fatal("stale client should be pruned") + } + if _, ok := limiter.clients["10.0.0.2"]; !ok { + t.Fatal("recent client should remain") + } +} + func TestRateLimitMiddlewareReturns429(t *testing.T) { now := time.Date(2026, 5, 28, 0, 0, 0, 0, time.UTC) limiter := newClientRateLimiter(1, time.Minute) diff --git a/beamsync/server.go b/beamsync/server.go index 3491dde..d44c79b 100644 --- a/beamsync/server.go +++ b/beamsync/server.go @@ -158,14 +158,58 @@ type clientRateLimiter struct { now func() time.Time } +const clientRateLimiterPruneInterval = 2 * time.Minute + +var clientRateLimiterPruner = struct { + once sync.Once + mu sync.Mutex + limiters map[*clientRateLimiter]struct{} +}{ + limiters: make(map[*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, } + registerClientRateLimiter(limiter) + return limiter +} + +func registerClientRateLimiter(limiter *clientRateLimiter) { + if limiter == nil { + return + } + + clientRateLimiterPruner.mu.Lock() + clientRateLimiterPruner.limiters[limiter] = struct{}{} + clientRateLimiterPruner.mu.Unlock() + + clientRateLimiterPruner.once.Do(func() { + go runClientRateLimiterPruner() + }) +} + +func runClientRateLimiterPruner() { + ticker := time.NewTicker(clientRateLimiterPruneInterval) + defer ticker.Stop() + + for now := range ticker.C { + clientRateLimiterPruner.mu.Lock() + limiters := make([]*clientRateLimiter, 0, len(clientRateLimiterPruner.limiters)) + for limiter := range clientRateLimiterPruner.limiters { + limiters = append(limiters, limiter) + } + clientRateLimiterPruner.mu.Unlock() + + for _, limiter := range limiters { + limiter.prune(now) + } + } } func (l *clientRateLimiter) allow(client string) rateLimitDecision { @@ -178,8 +222,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 { From f73f8a51a95f3fefbadf2d603198a3e5a9ef0f6f Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Bajpai Date: Sun, 21 Jun 2026 01:01:08 +0530 Subject: [PATCH 2/2] debounce qr generation --- desktop/frontend/src/App.svelte | 34 +++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/desktop/frontend/src/App.svelte b/desktop/frontend/src/App.svelte index bcaec00..12abb84 100644 --- a/desktop/frontend/src/App.svelte +++ b/desktop/frontend/src/App.svelte @@ -68,6 +68,7 @@ let transferStatsNow = Date.now(); let transferStatsTimer; let transferStatsThrottleTimer; + let qrGenerationTimer = null; let pendingTransferStats = null; let lastTransferStatsUpdateAt = 0; let transferSpeeds = { @@ -482,6 +483,7 @@ clearTimeout(batchTimer); clearTimeout(_progressTimeout); clearTimeout(transferStatsThrottleTimer); + clearTimeout(qrGenerationTimer); clearInterval(transferStatsTimer); }); @@ -550,18 +552,26 @@ } function generateQR(text) { - if (!text) return; - QRCode.toDataURL( - text, - { - width: 220, - margin: 2, - color: { dark: "#0A0A0A", light: "#00000000" }, - }, - (err, url) => { - if (!err) qrImage = url; - }, - ); + clearTimeout(qrGenerationTimer); + + if (!text) { + qrImage = ""; + return; + } + + qrGenerationTimer = setTimeout(() => { + QRCode.toDataURL( + text, + { + width: 220, + margin: 2, + color: { dark: "#0A0A0A", light: "#00000000" }, + }, + (err, url) => { + if (!err) qrImage = url; + }, + ); + }, 100); } function playSound(type) {