You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(proxy): reserve all standalone TPM counters in one Lua call - #46494
Concurrent requests on a team model_tpm_limit can all get 429, even when there's room for one
test_concurrent_team_model_tpm_requests_reserve_tokens_before_reaching_the_provider flaked on main in 6 of the last 22 CircleCI runs, always with (429, 429, 429)
How it solves it:
On standalone Redis, the TPM reservation for every counter (model_per_key, model_per_team, ...) runs as one Lua call, so it's all-or-nothing again and still one round trip
One comes back 200 with provider usage and the other two get 429 Limit type: tokens. Current limit: 100
Affected release
Regression from #43407 (request-scoped Redis pipeline, merged 2026-09-29)
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
I have added meaningful tests
The handful of test files covering my change pass locally, e.g. uv run pytest tests/unit/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
My PR's scope is as isolated as possible; it only solves 1 specific problem
Every automated review on my PR's latest commit has finished, and I have addressed or replied to each of its findings before requesting a maintainer review
Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
This is a race inside the rate limiter, so the proof is the existing integration test looped against one live proxy with real Postgres and Redis and a scripted upstream. Each iteration fires the test's 3 concurrent requests
Leg
Code
Runs
Failed
A
main (457b5d26c7)
50 serial
1, with (429, 429, 429) and Remaining: 28 on both model_per_key and model_per_team
B
main with the request pipeline forced off
50 serial
0
C
this branch
50 serial
0
C
this branch
100, 2 loops in parallel
0
A debug log on leg A showed all 150 requests had a request batch open and 2 descriptor groups (model_per_key, model_per_team), so every reservation was 2 separate Lua calls in one pipeline
Caveats
On Redis Cluster, a request with more than one rate-limited counter now pays one round trip per counter for its reservation, which is what it paid before #43407. Those counters live on different slots, so one atomic call isn't possible there
_settle_pipelined_descriptor_groups now only ever gets one group, so its multi-group refund handling is unreachable. I left it in place to keep this diff small
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You have signed the CLA already but the status is still pending? Let us recheck it.
⚠️1 benchmark spent significant time in system calls
System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.
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
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.
TLDR
Problem this solves:
model_tpm_limitcan all get 429, even when there's room for onetest_concurrent_team_model_tpm_requests_reserve_tokens_before_reaching_the_providerflaked on main in 6 of the last 22 CircleCI runs, always with(429, 429, 429)How it solves it:
model_per_key,model_per_team, ...) runs as one Lua call, so it's all-or-nothing again and still one round tripforce_redis_hash_tag_grouping, multi-counter reservations skip the request pipeline and run one counter at a time, stopping at the first one over the limit (same as before perf(proxy): one request-scoped Redis pipeline for auth, spend, rate-limit and routing reads #43407)User Flow
Before: a team with a per-model TPM limit sees every concurrent request rejected, even though the limit has room for one
{"model_tpm_limit": {"gpt-4o-mini": 100}}and gives a key to that team"max_tokens": 60Rate limit exceeded for model_per_key ... Remaining: 28and none reach the providerAfter: one request is served and the other two are rate limited
{"model_tpm_limit": {"gpt-4o-mini": 100}}and gives a key to that team"max_tokens": 60Limit type: tokens. Current limit: 100Affected release
Regression from #43407 (request-scoped Redis pipeline, merged 2026-09-29)
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/unit/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or moreDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
This is a race inside the rate limiter, so the proof is the existing integration test looped against one live proxy with real Postgres and Redis and a scripted upstream. Each iteration fires the test's 3 concurrent requests
457b5d26c7)(429, 429, 429)andRemaining: 28on bothmodel_per_keyandmodel_per_teamA debug log on leg A showed all 150 requests had a request batch open and 2 descriptor groups (
model_per_key,model_per_team), so every reservation was 2 separate Lua calls in one pipelineCaveats
On Redis Cluster, a request with more than one rate-limited counter now pays one round trip per counter for its reservation, which is what it paid before #43407. Those counters live on different slots, so one atomic call isn't possible there
_settle_pipelined_descriptor_groupsnow only ever gets one group, so its multi-group refund handling is unreachable. I left it in place to keep this diff smallLink to Devin session: https://app.devin.ai/sessions/b692f38aa3154bac8d195178279a7b1c
Open in Devin Desktop: https://app.devin.ai/desktop/session/b692f38aa3154bac8d195178279a7b1c?variant=devin
Requested by: @kerrylu-berri