{10x}debuggable
Rate Limiter Service
bugintermediateP1pkg/ratelimit/janitor.goLevel 1 · Fix a bug15 XP45–90 minutes

Janitor evicts active keys almost immediately

Nobody has started this yet — be first.

Business impact

Rate-limit tiers are how the product enforces its pricing model 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. 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.

Problem

The janitor is supposed to delete only keys idle for longer than idleTTL. Instead it deletes keys almost immediately after they're created or touched, well before idleTTL has elapsed.

Current behavior

newJanitor's sweep computes a cutoff that ends up in the future relative to "now," not the past -- so every entry's last-access timestamp 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, 2time.Second / idleTTL */) tb.Allow(ctx, "user-1") // consumes the only token -> Allowed: true res, _ := tb.Allow(ctx, "user-1") // res.Allowed == false, correctly denied time.Sleep(1300 * time.Millisecond) // well under idleTTL=2s, past at least one sweep tick res, _ = tb.Allow(ctx, "user-1") // got: Allowed: true -- the janitor silently evicted the entry and it was recreated fresh // want: Allowed: false -- only ~1.3s of a 2s idleTTL had elapsed; the key was NOT idle

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, IDLE_TTL=1h (the documented default) would behave nothing like advertised.

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

  • A key touched more recently than idleTTL ago survives any number of janitor sweep ticks
  • A key genuinely untouched for longer than idleTTL is still evicted (don't just remove the eviction entirely)
  • go test ./test/ -run TestTask06 -v passes

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.

01

Branch off your fork

$git checkout -b fix/janitor-cutoff-sign
02

Fix it and commit

Meet every acceptance criterion, and add a test that would have caught this.

03

Push the branch

$git push -u origin fix/janitor-cutoff-sign
04

Submit it below

Paste your fork URL and the branch name, with a short write-up of the root cause.

Submit your fix

Sign in to submit a solution and track your progress.

Sign in to submit

Questions

Stuck on something?

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 in
No questions yet. If something in this ticket reads ambiguously, you are probably not the only one — ask.