Skip to content

[Fix] Refresh Redis TTL on counter writes, skip stale in-memory in Redis - #26829

Merged
Michael-RZ-Berri merged 4 commits into
litellm_internal_stagingfrom
litellm_budgetEnforcementMultiPod
May 1, 2026
Merged

[Fix] Refresh Redis TTL on counter writes, skip stale in-memory in Redis#26829
Michael-RZ-Berri merged 4 commits into
litellm_internal_stagingfrom
litellm_budgetEnforcementMultiPod

Conversation

@Michael-RZ-Berri

@Michael-RZ-Berri Michael-RZ-Berri commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Budgets can still be bypassed on multi-pod / multi-worker setups because of how caching is organized, and pods not communicating spending to eachother. This PR does a few things to enforce budgets more correctly on multi-pod:

  • get_current_spend and SpendCounterReseed.coalesced skip stale per-pod in-memory on a clean Redis miss; reseed from DB instead.

  • RedisCache.async_increment gains refresh_ttl opt-in. Spend counters now use it so the one minute TTL extends / resets when the counter is being hit, other keys keep the previous set-once behavior. This is for performance reasons (keys accessed often avoid pinging the DB too much, especially in multi-pod).

  • ResetBudgetJob invalidates the Redis counter alongside every DB row reset (keys, users, teams, team members, budgets-linked keys), so refreshed counters still get reset.

Linear ticket

Resolves LIT-2466.

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

Before:
Screenshot 2026-04-29 at 3 19 39 PM

After:
Screenshot 2026-04-29 at 3 19 50 PM

Type

🐛 Bug Fix
🚄 Infrastructure
✅ Test

Changes

dual_cache, redis_cache, reset_budget_job, spend_counter_reseed, proxy_server

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
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.


Michael Riad Zaky seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@Michael-RZ-Berri

Copy link
Copy Markdown
Contributor Author

@greptile-ai

@Michael-RZ-Berri Michael-RZ-Berri changed the title Refresh Redis TTL on counter writes, skip stale in-memory in Redis [Fix] Refresh Redis TTL on counter writes, skip stale in-memory in Redis Apr 30, 2026
@Michael-RZ-Berri
Michael-RZ-Berri force-pushed the litellm_budgetEnforcementMultiPod branch from 8682528 to b47d1e1 Compare April 30, 2026 00:27
@Michael-RZ-Berri

Copy link
Copy Markdown
Contributor Author

@greptile-ai

@codecov

codecov Bot commented Apr 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.75510% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/proxy/common_utils/reset_budget_job.py 89.74% 4 Missing ⚠️
litellm/caching/redis_cache.py 75.00% 1 Missing ⚠️
litellm/proxy/db/spend_counter_reseed.py 83.33% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes multi-pod budget bypass by (1) skipping stale per-pod in-memory on a clean Redis miss and reseeding from DB instead, (2) adding refresh_ttl=True so spend-counter TTLs are extended on every write rather than only at first creation, and (3) correctly ordering Redis counter invalidation after the DB row reset in ResetBudgetJob for all entity types.

The DB-write-first ordering is now consistently applied across keys, users, teams, team members, and linked-budget keys. Two minor P2 findings: the find_many exception in reset_budget_for_litellm_team_members is silently swallowed, which can leave stale Redis counters after a successful DB reset in transient-failure scenarios; and the sequential _invalidate_spend_counter loop could be parallelised with asyncio.gather for large batches.

Confidence Score: 5/5

Safe to merge — no security bypasses or data-loss paths introduced; both findings are P2.

All P1 concerns from prior review rounds (DB-before-Redis ordering, explicit TTL on reset writes) have been addressed. The two remaining findings are P2: a silent exception swallow that causes over-enforcement (not bypass) in a transient DB failure, and a sequential loop that could be batched. No P0 or P1 issues are present.

litellm/proxy/common_utils/reset_budget_job.py — the find_many exception handling in reset_budget_for_litellm_team_members is worth a second look.

Important Files Changed

Filename Overview
litellm/caching/redis_cache.py Adds refresh_ttl flag to async_increment; when True, calls expire() unconditionally on every write instead of only on initial creation. Logic is correct and backward-compatible.
litellm/caching/dual_cache.py Threads refresh_ttl kwarg through async_increment_cache to the underlying Redis layer. Minimal, correct change.
litellm/proxy/proxy_server.py Introduces redis_clean_miss flag in get_current_spend to skip stale per-pod in-memory on a clean Redis None; passes refresh_ttl=True on all spend counter increments. Correctly handles single-pod (no Redis) and multi-pod cases.
litellm/proxy/db/spend_counter_reseed.py Mirrors the redis_clean_miss pattern in coalesced and uses refresh_ttl=True when warming the cache from DB. Correctly skips stale in-memory on a clean Redis miss.
litellm/proxy/common_utils/reset_budget_job.py Introduces _invalidate_spend_counter and correctly orders DB-write-then-Redis-invalidate for keys, users, teams, linked-budget keys, and team members. The find_many exception swallow in reset_budget_for_litellm_team_members can leave stale Redis counters after a successful DB reset; sequential invalidation loops could be batched.
tests/test_litellm/caching/test_redis_cache.py Adds two clean unit tests for refresh_ttl behaviour — one verifying expire() is called unconditionally, one verifying it is skipped on the default path. All mock-only, no real network calls.
tests/test_litellm/proxy/common_utils/test_reset_budget_job.py Adds five counter-invalidation tests covering team-member, key, user, team, and linked-budget paths. All use mocks, no real network calls.
tests/test_litellm/proxy/test_proxy_server.py Adds two tests verifying the redis_clean_miss behaviour in get_current_spend: clean Redis None triggers DB reseed, Redis error falls back to in-memory. Both are well-structured and mock-only.

Reviews (2): Last reviewed commit: "Reorder counter invalidation to run afte..." | Re-trigger Greptile

Comment thread litellm/proxy/db/spend_counter_reseed.py
Comment thread litellm/proxy/common_utils/reset_budget_job.py
@Michael-RZ-Berri
Michael-RZ-Berri force-pushed the litellm_budgetEnforcementMultiPod branch from b47d1e1 to 55d6dc8 Compare April 30, 2026 01:32
@Michael-RZ-Berri

Copy link
Copy Markdown
Contributor Author

@greptile-ai

@Michael-RZ-Berri
Michael-RZ-Berri force-pushed the litellm_budgetEnforcementMultiPod branch 4 times, most recently from 0a5c094 to e8f88cd Compare May 1, 2026 00:28
@Michael-RZ-Berri
Michael-RZ-Berri force-pushed the litellm_budgetEnforcementMultiPod branch from e8f88cd to 4e26835 Compare May 1, 2026 00:51

@ryan-crabbe-berri ryan-crabbe-berri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Michael-RZ-Berri
Michael-RZ-Berri merged commit 05e6402 into litellm_internal_staging May 1, 2026
116 checks passed
@Michael-RZ-Berri
Michael-RZ-Berri deleted the litellm_budgetEnforcementMultiPod branch May 1, 2026 01:14
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…ntMultiPod

[Fix] Refresh Redis TTL on counter writes, skip stale in-memory in Redis
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants