Nobody has started this yet — be first.
Business impact
Rate-limit tiers are how the product enforces its pricing model (e.g. free-tier: 10 requests/minute) and how infrastructure cost stays bounded under abuse. If configured state resets far more often than IDLE_TTL promises, free-tier usage stops being meaningfully capped -- costs that the pricing model assumed would be limited are not, and paying customers on higher tiers get no differentiated value from the tier they're paying for. Because this bug produces more allowed traffic rather than errors, it is one of the quietest possible failures to notice: nothing looks broken from the outside, the bill or the abuse just creeps up over time.
Problem
The janitor is supposed to delete only keys that have been idle for longer than idleTTL. Instead, it deletes keys almost immediately after they're created or touched -- well before idleTTL has actually elapsed -- which silently resets rate-limit state far too early.
Current behavior
newJanitor's sweep computes a cutoff and deletes any entry whose idleSince() is less than cutoff. With the current cutoff computation, cutoff ends up in the future relative to "now," so essentially every entry looks "idle" on the very next sweep tick, no matter how recently it was actually touched.
Expected behavior
Only entries whose last access was more than idleTTL in the past should be deleted. An entry touched within the last idleTTL must survive any number of sweep ticks.
Steps to reproduce
tb := ratelimit.NewTokenBucketMemory(1, 0 /* no refill /, 2time.Second /* idleTTL */) tb.Allow(ctx, "user-1") // consumes the only token -> Allowed: true tb.Allow(ctx, "user-1") // Allowed: false, correctly denied (exhausted, no refill) time.Sleep(1300 * time.Millisecond) // well under idleTTL=2s, past at least one janitor sweep tick tb.Allow(ctx, "user-1") // got: Allowed: true -- the janitor silently evicted the entry and it was recreated fresh // want: Allowed: false -- the key was touched 1.3s ago, well inside the 2s idle window
Why this matters
This turns "idle key garbage collection" into "state silently resets on a timer regardless of activity," for every algorithm and both backends that embed a janitor. In production this means a client's rate limit quietly refreshes far more often than configured -- IDLE_TTL=1h (the documented default) would behave nothing like advertised, undermining every limit in the service.
Suggested approach
Look at how cutoff is derived from time.Now() and j.idleTTL in (*janitor).run, and compare it to what "idle for longer than idleTTL" actually requires arithmetically (a point in the past, idleTTL behind now -- not ahead of it).
Acceptance criteria
Verification
go test ./test/ -run TestTask06 -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/janitor-cutoff-signFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/janitor-cutoff-signSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inhttp://localhost:8080localhost:6380http://localhost:8081curl localhost:8080/healthz
curl localhost:8080/api/v1/rules
curl -s -X POST localhost:8080/api/v1/check -d '{"rule":"free-tier","key":"user-123"}' | jq
No Docker? STORE_BACKEND=memory is the default, so go run ./cmd/server alone (no Redis needed) exercises everything except the Redis-backed code paths.
Work the tickets in tasks/ (ordered easy → hard); each names one Go test in test/:
make test-race # go test -race -count=1 ./...
go test ./test/ -run TestTask01 -v # a single ticket
./test/run_all.sh # all 15, pass/fail summary
Level 1
Fix a bug
Read existing behaviour, correct it.