Nobody has started this yet — be first.
Business impact
Right now the only rate-limit signal exposed via /metrics is a counter of rejections that already happened. There is no way for an on-call dashboard or alert to see a client approaching its limit before it actually gets throttled -- graphing "client X is down to 2 tokens, about to get cut off" is impossible today; the only visibility is after the 429s start, which is too late to react proactively.
Problem
RateLimiter.check() already computes tokens_remaining on every call -- it is right there in the the proxy handler gets back -- but that number is thrown away the moment the handler finishes reading . There is a Gauge already defined in , labeled by and , so it shows up in ' exposition format -- nothing anywhere calls on it.
Current behavior
The rate_limit_tokens_remaining Gauge always reads 0 for every route/client combination, regardless of how many tokens are actually left, because no call site anywhere invokes .labels(...).set(...) on it.
Expected behavior
Every time a rate-limited route's request goes through the rate-limit check in app/routers/proxy.py, rate_limit_tokens_remaining for that (route_id, client_key) pair is updated to whatever RateLimitResult.tokens_remaining reports -- whether the request was allowed or rejected.
Steps to reproduce
pytest practice_tickets/tests/test_ticket_03_rate_limit_tokens_remaining_metric.py -v.Why this matters
The metric and its .labels(...).set(...) call signature both already exist in app/metrics.py -- what is missing is wiring it up at the one call site in app/routers/proxy.py that already has result (the RateLimitResult) in scope after calling rate_limiter.check(...). Nothing about the rate limiter itself needs to change.
Suggested approach
Look at where app/routers/proxy.py calls rate_limiter.check(...) and already has result in scope. This is a one-line wiring job at the call site, not new plumbing -- both the metric and its update signature already exist.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_03_rate_limit_tokens_remaining_metric.py -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/rate-limit-tokens-remaining-metricFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/rate-limit-tokens-remaining-metricSubmit 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 inRateLimitResultresult.allowedrate_limit_tokens_remainingapp/metrics.pyroute_idclient_key/metrics.set()http://localhost:8080fast_echo/echo/.../large/{size_mb}slow_upstream/delay/{s}/flaky/always-downroutes.yamlcurl localhost:8080/healthzcurl localhost:8080/readyz/admin/*Authorization: Bearer <GATEWAY_ADMIN_TOKEN>You do not need Docker running at all to work the tickets — the test suite fakes Redis with fakeredis (plus lupa for real Lua-script support), so everything runs against a plain virtualenv:
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/pytest practice_tickets/tests/test_ticket_09_breaker_threshold_off_by_one.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
./practice_tickets/run_tickets.sh 06 09 15 # just a few, by number
All 15 tests fail at the start; as you fix each ticket in app/, its test (and only its test) should flip to passing. .venv/bin/pytest tests/ runs the project's own main suite — as of writing it shows 8 pre-existing failures caused by ticket bugs 02, 04, 06, and 09; fixing those tickets fixes those failures too, not a separate task.
Level 2
Implement a feature
Extend the system within its own patterns.