mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
fix(proxy): add is_database_transport_error for reconnect logic, restore broad is_database_connection_error
#21706 narrowed is_database_connection_error to only match PrismaErrors with connectivity keywords. That was correct for the reconnect use case but broke the allow_requests_on_db_unavailable behavior as a side effect. Fix: add is_database_transport_error with the narrow keyword-gated check and use it in the two reconnect call sites (auth_checks key lookup retry, utils.py watchdog). Restore is_database_connection_error to its original broad behavior (any PrismaError) so allow_requests_on_db_unavailable works correctly. Update the tests added in #21706 to assert against is_database_transport_error instead of is_database_connection_error.
This commit is contained in:
parent
a5e886de79
commit
6b818ab3a1
5 changed files with 30 additions and 10 deletions
|
|
@ -2000,7 +2000,7 @@ async def _fetch_key_object_from_db_with_reconnect(
|
|||
proxy_logging_obj=proxy_logging_obj,
|
||||
)
|
||||
except Exception as e:
|
||||
if PrismaDBExceptionHandler.is_database_connection_error(e):
|
||||
if PrismaDBExceptionHandler.is_database_transport_error(e):
|
||||
did_reconnect = False
|
||||
if hasattr(prisma_client, "attempt_db_reconnect"):
|
||||
auth_reconnect_timeout = getattr(
|
||||
|
|
|
|||
|
|
@ -32,7 +32,28 @@ class PrismaDBExceptionHandler:
|
|||
@staticmethod
|
||||
def is_database_connection_error(e: Exception) -> bool:
|
||||
"""
|
||||
Returns True if the exception is from a database outage / connection error
|
||||
Returns True if the exception is from a database outage / connection error.
|
||||
Any PrismaError qualifies — the DB failed to serve the request.
|
||||
Used by allow_requests_on_db_unavailable logic.
|
||||
"""
|
||||
import prisma
|
||||
|
||||
if isinstance(e, DB_CONNECTION_ERROR_TYPES):
|
||||
return True
|
||||
if isinstance(e, prisma.errors.PrismaError):
|
||||
return True
|
||||
if isinstance(e, ProxyException) and e.type == ProxyErrorTypes.no_db_connection:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_database_transport_error(e: Exception) -> bool:
|
||||
"""
|
||||
Returns True only for transport/connectivity failures where a reconnect
|
||||
attempt makes sense (e.g. DB is unreachable, connection dropped).
|
||||
|
||||
Use this for reconnect logic — data-layer errors like UniqueViolationError
|
||||
mean the DB IS reachable, so reconnecting would be pointless.
|
||||
"""
|
||||
import prisma
|
||||
|
||||
|
|
@ -44,8 +65,6 @@ class PrismaDBExceptionHandler:
|
|||
return True
|
||||
if isinstance(e, prisma.errors.PrismaError):
|
||||
error_message = str(e).lower()
|
||||
# Treat generic PrismaError as connection error only when its text
|
||||
# clearly indicates transport/connectivity failure.
|
||||
connection_keywords = (
|
||||
"can't reach database server",
|
||||
"cannot reach database server",
|
||||
|
|
|
|||
|
|
@ -3578,8 +3578,8 @@ class PrismaClient:
|
|||
"""
|
||||
try:
|
||||
engine = self.db._original_prisma._engine # type: ignore[attr-defined]
|
||||
if engine is not None and engine.process is not None:
|
||||
return engine.process.pid
|
||||
if engine is not None and engine.process is not None: # type: ignore[union-attr]
|
||||
return engine.process.pid # type: ignore[union-attr]
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
|
||||
|
|
@ -4125,7 +4125,7 @@ class PrismaClient:
|
|||
except Exception as e:
|
||||
if isinstance(
|
||||
e, asyncio.TimeoutError
|
||||
) or PrismaDBExceptionHandler.is_database_connection_error(e):
|
||||
) or PrismaDBExceptionHandler.is_database_transport_error(e):
|
||||
await self.attempt_db_reconnect(
|
||||
reason="db_health_watchdog_connection_error",
|
||||
timeout_seconds=self._db_watchdog_reconnect_timeout_seconds,
|
||||
|
|
|
|||
|
|
@ -72,8 +72,9 @@ def test_is_database_connection_error_prisma_connection_errors(prisma_error):
|
|||
),
|
||||
],
|
||||
)
|
||||
def test_is_database_connection_error_non_connection_prisma_errors(prisma_error):
|
||||
assert PrismaDBExceptionHandler.is_database_connection_error(prisma_error) == False
|
||||
def test_is_database_transport_error_non_connection_prisma_errors(prisma_error):
|
||||
"""Data-layer errors should not trigger reconnect — DB is reachable when these occur."""
|
||||
assert PrismaDBExceptionHandler.is_database_transport_error(prisma_error) == False
|
||||
|
||||
|
||||
def test_is_database_connection_generic_errors():
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ async def test_db_health_watchdog_should_trigger_reconnect_on_db_error(mock_prox
|
|||
"litellm.proxy.utils.asyncio.sleep",
|
||||
AsyncMock(side_effect=[None, asyncio.CancelledError()]),
|
||||
), patch(
|
||||
"litellm.proxy.db.exception_handler.PrismaDBExceptionHandler.is_database_connection_error",
|
||||
"litellm.proxy.db.exception_handler.PrismaDBExceptionHandler.is_database_transport_error",
|
||||
return_value=True,
|
||||
):
|
||||
await client._db_health_watchdog_loop()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue