Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion litellm/caching/redis_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -1270,7 +1270,8 @@ def flushall(self):
self.redis_client.flushall()

async def disconnect(self):
await self.async_redis_conn_pool.disconnect(inuse_connections=True)
if self.async_redis_conn_pool is not None:
await self.async_redis_conn_pool.disconnect(inuse_connections=True)
try:
self.redis_client.close()
except Exception as e:
Expand Down
26 changes: 26 additions & 0 deletions tests/test_litellm/caching/test_redis_connection_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,29 @@ async def test_disconnect_idempotent():

await cache.disconnect()
await cache.disconnect() # should not raise


@pytest.mark.asyncio
async def test_disconnect_when_async_pool_is_none():
"""Regression: disconnect() must not raise AttributeError when
async_redis_conn_pool is None (cluster-mode path sets it to None)."""
cache, mock_sync_client, _ = _make_redis_cache()
# Simulate cluster mode: connection pool is None after construction
cache.async_redis_conn_pool = None

# Should complete without raising AttributeError
await cache.disconnect()

# Sync client cleanup is still attempted
mock_sync_client.close.assert_called_once()


@pytest.mark.asyncio
async def test_disconnect_with_pool_calls_pool_disconnect():
"""Happy path: when async_redis_conn_pool is present, disconnect()
forwards the call with inuse_connections=True."""
cache, mock_sync_client, mock_async_pool = _make_redis_cache()
await cache.disconnect()

mock_async_pool.disconnect.assert_awaited_once_with(inuse_connections=True)
mock_sync_client.close.assert_called_once()