fix(proxy): fall back to pod-local spend when Redis read returns nothing

This commit is contained in:
Devin AI 2026-07-15 02:30:20 +00:00
parent 3ce69f0583
commit b7b5002961
2 changed files with 41 additions and 4 deletions

View file

@ -152,13 +152,16 @@ class _PROXY_VirtualKeyModelMaxBudgetLimiter(RouterBudgetLimiting):
A pod-local in-memory value can lag the shared total, so `async_get_cache`
(which returns an in-memory hit before consulting Redis) must not be used
here. Local memory is only read as a fallback when Redis is not configured.
`redis_cache.async_get_cache` already handles its own connection errors and
returns None, so no additional error handling is needed.
here. Redis is read first; the pod-local value is only used as a fallback
when Redis is not configured or returns nothing (for example during a Redis
outage, since `redis_cache.async_get_cache` swallows connection errors and
returns None), which preserves per-pod enforcement instead of failing open.
"""
redis_cache = self.dual_cache.redis_cache
if redis_cache is not None:
return await redis_cache.async_get_cache(key=cache_key)
redis_spend = await redis_cache.async_get_cache(key=cache_key)
if redis_spend is not None:
return redis_spend
return await self.dual_cache.async_get_cache(key=cache_key, local_only=True)
async def _get_end_user_spend_for_model(

View file

@ -676,3 +676,37 @@ async def test_admission_falls_back_to_local_spend_when_redis_unavailable():
with pytest.raises(litellm.BudgetExceededError):
await limiter.is_key_within_model_budget(user_api_key, model)
@pytest.mark.asyncio
async def test_admission_falls_back_to_local_spend_when_redis_returns_nothing():
"""
Redis is configured but unreachable (its get swallows the error and returns
None). The limiter must fall back to the pod-local spend and keep enforcing
per-pod, rather than failing open and admitting every request.
"""
budget = 100.0
local_spend = 150.0
model = "gpt-4"
budget_duration = "1d"
token = "test-key"
cache_key = f"{VIRTUAL_KEY_SPEND_CACHE_KEY_PREFIX}:{token}:{model}:{budget_duration}"
unreachable_redis = _SharedRedisCache(store={})
dual_cache = DualCache()
dual_cache.redis_cache = unreachable_redis
await dual_cache.async_set_cache(key=cache_key, value=local_spend, local_only=True)
limiter = _PROXY_VirtualKeyModelMaxBudgetLimiter(dual_cache=dual_cache)
user_api_key = UserAPIKeyAuth(
token=token,
key_alias="test-alias",
model_max_budget={model: {"budget_limit": budget, "time_period": budget_duration}},
)
with pytest.raises(litellm.BudgetExceededError) as exc_info:
await limiter.is_key_within_model_budget(user_api_key, model)
assert exc_info.value.current_cost == local_spend
assert unreachable_redis.get_count >= 1