mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix(proxy): fall back to pod-local spend when Redis read returns nothing
This commit is contained in:
parent
3ce69f0583
commit
b7b5002961
2 changed files with 41 additions and 4 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue