mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix(proxy): write the Prisma engine through the public setter, not the mangled name
`_write_engine` assigned directly to `prisma_client._Prisma__engine`, the name Prisma <0.13 used internally for the engine attribute. Prisma 0.13+ is a `__slots__` class that no longer has that attribute at all, only a public `_engine` property/setter backed by `_internal_engine`. Writing to the mangled name on a modern client raises `AttributeError`, which `connect()`'s `backoff` decorator retries indefinitely, spinning CPU and respawning Prisma engine subprocesses without ever connecting. Use the public `_engine` setter instead, which exists on both the legacy and current Prisma internal layouts. Reproduced locally, prisma==0.15.0: router at 196-293% CPU with a continuous `AttributeError: 'Prisma' object has no attribute '_Prisma__engine'` retry loop; 0% after the fix, connects immediately.
This commit is contained in:
parent
ec3f8183c3
commit
6a5632c377
3 changed files with 35 additions and 7 deletions
|
|
@ -53,10 +53,7 @@ class _PrismaEngine(Protocol):
|
|||
|
||||
|
||||
class _PrismaClient(Protocol):
|
||||
_Prisma__engine: _PrismaEngine
|
||||
|
||||
@property
|
||||
def _engine(self) -> _PrismaEngine: ...
|
||||
_engine: _PrismaEngine
|
||||
|
||||
|
||||
class _PrismaDrainTracker:
|
||||
|
|
@ -240,7 +237,9 @@ class PrismaWrapper:
|
|||
|
||||
@staticmethod
|
||||
def _write_engine(prisma_client: _PrismaClient, engine: _PrismaEngine) -> None:
|
||||
prisma_client._Prisma__engine = engine
|
||||
# Public setter: prisma <0.13 stored the engine on the mangled `Prisma.__engine`,
|
||||
# 0.13+ on `BasePrisma._internal_engine`. Both are __slots__ classes.
|
||||
prisma_client._engine = engine # rebind-ok: third-party Prisma client instance, not a local param we own
|
||||
|
||||
def _instrument_prisma_client(self, prisma_client: _PrismaClient) -> _PrismaDrainTracker | None:
|
||||
from prisma.errors import ClientNotConnectedError
|
||||
|
|
|
|||
|
|
@ -171,6 +171,35 @@ def test_get_engine_pid_returns_zero_for_disconnected_client(disconnected_prisma
|
|||
assert wrapper._get_engine_pid() == 0
|
||||
|
||||
|
||||
def test_write_engine_uses_the_public_setter_not_the_mangled_name():
|
||||
"""Real Prisma clients (>=0.13) are __slots__ classes exposing only a public
|
||||
`_engine` property/setter, with no `_Prisma__engine` slot. Writing to the
|
||||
mangled name raises AttributeError instead of storing the engine."""
|
||||
|
||||
class _FakeSlotsBasedPrismaClient:
|
||||
__slots__ = ("_internal_engine",)
|
||||
|
||||
def __init__(self):
|
||||
self._internal_engine = None
|
||||
|
||||
@property
|
||||
def _engine(self):
|
||||
if self._internal_engine is None:
|
||||
raise AttributeError("not connected")
|
||||
return self._internal_engine
|
||||
|
||||
@_engine.setter
|
||||
def _engine(self, engine):
|
||||
self._internal_engine = engine
|
||||
|
||||
client = _FakeSlotsBasedPrismaClient()
|
||||
sentinel = object()
|
||||
|
||||
PrismaWrapper._write_engine(client, sentinel)
|
||||
|
||||
assert PrismaWrapper._read_engine(client) is sentinel
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_recreate_prisma_client_recovers_from_disconnected_client(
|
||||
mock_prisma_binary, disconnected_prisma
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ def _make_wrapper(engine_pid: int = 111, iam: bool = False) -> PrismaWrapper:
|
|||
prisma = GeneratedPrisma(use_dotenv=False)
|
||||
engine = MagicMock()
|
||||
engine.process.pid = engine_pid
|
||||
setattr(prisma, "_Prisma__engine", engine)
|
||||
prisma._engine = engine
|
||||
return PrismaWrapper(original_prisma=prisma, iam_token_db_auth=iam)
|
||||
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ def _token_db_url(created: datetime, expires_in: int = 900) -> str:
|
|||
def test_wrapper_instruments_generated_prisma_engine() -> None:
|
||||
prisma = GeneratedPrisma(use_dotenv=False)
|
||||
engine = MagicMock()
|
||||
setattr(prisma, "_Prisma__engine", engine)
|
||||
prisma._engine = engine
|
||||
|
||||
wrapper = PrismaWrapper(original_prisma=prisma, iam_token_db_auth=False)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue