Bug: Fix memory leak in rate limiter cleanup - #208
Merged
parkerwinner merged 2 commits intoJul 30, 2026
Merged
Conversation
The in-memory rate limiter already had a background cleanup goroutine (contrary to the issue's premise that none existed) — GetRateLimiter starts one via a sync.Once singleton, sweeping every 10 minutes and removing entries unaccessed for over an hour. Two real gaps remained: 1. The cleanup interval was 10 minutes, not the 5 minutes this issue's acceptance criteria call for. Fixed via a defaultCleanupInterval constant (also extracted defaultStaleAfter = 1 hour, the existing "remove after" threshold, previously hardcoded inline). 2. The cleanup goroutine had no way to be stopped — fine for the process-lifetime singleton in production, but it meant there was no way to write a deterministic test of the cleanup behavior itself (the singleton can't be reconfigured with short intervals without affecting every other test that calls GetRateLimiter). Added newRateLimiter (an unexported constructor separate from the GetRateLimiter singleton accessor) and Stop() so tests can build an independent RateLimiter with short, fast intervals and shut its goroutine down cleanly afterward. 5 new tests build a RateLimiter with millisecond-scale cleanupInterval/staleAfter and assert the actual runtime behavior: a stale entry is removed, a recently-accessed entry survives the same sweep, only the genuinely stale key among several is removed, Stop() terminates the goroutine, and the production defaults match the issue's stated 5-minute/1-hour values. All existing rate-limiter tests (10) still pass unchanged; the full suite passes under -race. ## Also fixed (blocking, unrelated) handlers/auth.go had a stray extra `})` after ResetPassword's closing brace — a hard syntax error preventing the entire backend module from building at all. Same root cause as parkerwinner#193's PR (independent branches off main both needed it fixed to build/test). ## Test plan - [x] `go build ./middleware/...` — clean - [x] `go test ./middleware/... -run RateLimit -race -v` — 15/15 passing Closes parkerwinner#196
|
@Emmanuelchukwunonso Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Owner
|
fix conflict |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The in-memory rate limiter already had a background cleanup goroutine (contrary to the issue's premise that none existed) —
GetRateLimiterstarts one via async.Oncesingleton, sweeping every 10 minutes and removing entries unaccessed for over an hour. Two real gaps remained:defaultCleanupIntervalconstant (also extracteddefaultStaleAfter = 1 hour, the existing "remove after" threshold, previously hardcoded inline).GetRateLimiter). AddednewRateLimiter(an unexported constructor separate from theGetRateLimitersingleton accessor) andStop()so tests can build an independentRateLimiterwith short, fast intervals and shut its goroutine down cleanly afterward.5 new tests build a
RateLimiterwith millisecond-scalecleanupInterval/staleAfterand assert the actual runtime behavior: a stale entry is removed, a recently-accessed entry survives the same sweep, only the genuinely stale key among several is removed,Stop()terminates the goroutine, and the production defaults match the issue's stated 5-minute/1-hour values. All 10 existing rate-limiter tests still pass unchanged; the full suite passes under-race.Also fixed (blocking, unrelated)
handlers/auth.gohad a stray extra})afterResetPassword's closing brace — a hard syntax error preventing the entire backend module from building at all. Same root cause as #193's PR (independent branches offmainboth needed it fixed to build/test).Test plan
go build ./middleware/...— cleango test ./middleware/... -run RateLimit -race -v— 15/15 passingCloses #196
Closes #194