Nobody has started this yet — be first.
Business impact
"A customer got wrongly throttled" is a routine support request, not an edge case -- shared office IPs, a customer's own retry storm, a bug on their side that looked like abuse from ours. Today the only remediation is "wait it out" or "restart the service" (which resets every customer's state, a blast-radius mismatch nobody should accept just to unblock one account). Every minute that customer is stuck is a minute of them unable to use the product, and every restart-as-a-fix is itself a minor self-inflicted incident. An operator-facing reset endpoint turns a multi-team escalation into a 30-second fix.
Problem
Once a key is rate-limited, there is no way to clear its state short of waiting out the window/refill, or restarting the process (memory backend) / manually deleting the Redis key with redis-cli (Redis backend). A Resetter contract has been added as scaffolding in pkg/ratelimit/limiter.go (Reset(ctx, key) error), but nothing implements it yet.
Current behavior
A key stuck in a rate-limited state cannot be cleared without waiting out its window/refill or restarting the whole process -- there is no per-key remediation available.
Expected behavior
Every one of the six limiter types (TokenBucketMemory, TokenBucketRedis, SlidingWindowMemory, SlidingWindowRedis, LeakyBucketMemory, LeakyBucketRedis) implements Resetter, restoring the key to a fresh/fully-available state. A new POST /api/v1/reset endpoint, body {"rule": "...", "key": "..."}, looks up the rule the same way /api/v1/check does, type-asserts its limiter to Resetter, and returns 204 on success, 404 for an unknown rule, and 501 if the limiter doesn't support Resetter. After a successful reset, the very next Allow/AllowN call for that key behaves exactly as if the key had never been used.
Steps to reproduce
Exhaust a rule's limit for a given key via repeated POST /api/v1/check calls. POST /api/v1/reset with {"rule": "...", "key": "..."} // got: 404 -- the route does not exist yet // want: 204, and the next check for that key behaves as if it were never used
Why this matters
Every production rate limiter needs an operator escape hatch. Without one, "a customer got wrongly throttled" turns into "wait or restart the service," neither of which is acceptable for anything but a toy.
Suggested approach
Memory implementations: the state lives in a sync.Map (buckets/entries) keyed by the same string key Allow/AllowN use -- Reset just needs to delete that entry so the next access recreates a fresh one via the existing LoadOrStore path already in AllowN. Redis implementations: Reset is a DEL on prefix + key, mirroring how the Lua scripts build their key -- check what your redis.Scripter field actually gives you access to, and whether the field type needs to change to expose Del without breaking the existing constructors' call sites in internal/rules/rules.go. Wire the new route in internal/api/router.go next to POST /api/v1/check, and the handler in internal/api/handlers.go next to handleCheck.
Acceptance criteria
Verification
go test ./test/ -run TestTask05 -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 feat/reset-endpointFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/reset-endpointSubmit 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 2
Implement a feature
Extend the system within its own patterns.