fix(redis): use a monotonic clock for the timeout log throttle and trim test docstrings

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yassin 2026-09-12 01:37:36 +00:00
parent 9c84e98fb2
commit f681a978f0
4 changed files with 4 additions and 20 deletions

View file

@ -409,7 +409,7 @@ class RedisCircuitBreakerOpenError(Exception):
class _RedisTimeoutLogThrottle:
"""Admits one Redis timeout log line per interval and counts the timeouts it suppressed in between."""
def __init__(self, interval: float, clock: Callable[[], float] = time.time) -> None:
def __init__(self, interval: float, clock: Callable[[], float] = time.monotonic) -> None:
self.interval = interval
self._clock = clock
self._lock = threading.Lock()

View file

@ -708,12 +708,7 @@ async def test_open_breaker_keeps_async_batch_read_memory_hits_and_releases_rese
@pytest.mark.asyncio
async def test_redis_timeouts_falling_back_to_memory_log_once_per_interval(caplog, monkeypatch):
"""The in-memory fallback WARNING must not repeat for every timed-out increment during a blip.
The rate limiter's pipeline increments and the dual cache increments each logged a WARNING per
call while Redis timed out, hundreds of lines per second before the breaker opened. The first
timeout of a streak keeps its WARNING, the rest are DEBUG until the summary interval passes.
"""
"""The first fallback WARNING of a timeout streak logs, the rest stay at DEBUG until the summary."""
from redis.exceptions import TimeoutError as RedisTimeoutError
from litellm.caching import redis_cache as redis_cache_module

View file

@ -1205,14 +1205,7 @@ async def test_a_probe_overtaken_by_a_later_outage_leaves_the_breaker_to_the_new
def test_timeouts_during_a_blip_log_once_per_interval_not_once_per_call(sync_batch_redis_cache, caplog, monkeypatch):
"""A Redis latency blip must not write one ERROR line per timed-out cache call.
Before the breaker opens (up to REDIS_CIRCUIT_BREAKER_TIMEOUT_MIN_DURATION of timeouts) every
cache operation logged its own ERROR or WARNING line, so one single-worker proxy wrote
~1100 lines in 5 s at LITELLM_LOG=WARNING. A timeout streak now logs its first failure, then
one summary line per REDIS_TIMEOUT_LOG_INTERVAL carrying the count of suppressed timeouts,
while every timeout stays visible at DEBUG. Hard connectivity failures keep their per-call line.
"""
"""A timeout streak logs its first failure plus one summary per interval; other failures log per call."""
import logging
from redis.exceptions import TimeoutError as RedisTimeoutError

View file

@ -1182,11 +1182,7 @@ async def test_apply_spend_counter_increments_open_breaker_invalidates_and_retur
@pytest.mark.asyncio
async def test_apply_spend_counter_increments_redis_timeout_invalidates_and_returns(monkeypatch):
"""A Redis timeout is the streak the breaker is already counting and the cache layer already logged.
Re-raising it sent every request in the pre-open window through the cost callback's error
path, which logged a traceback and fired the failed-tracking alert once per request.
"""
"""A Redis timeout invalidates the counters and returns without reaching the cost callback's error path."""
from redis.exceptions import TimeoutError as RedisTimeoutError
fake_cache = _make_spend_counter_cache()