Ë
    tPêj¤  ã                   ój  — d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZmZ ddlmZmZmZ ddlmZ ddlmZmZmZmZmZmZmZ ddlmZ d	Z G d
„ de«      Z G d„ de«      Z G d„ de«      Z  G d„ d«      Z! G d„ d«      Z" G d„ d«      Z# G d„ d«      Z$	 dde%e&z  dz  de%fd„Z'y)z“
This file contains the PrismaWrapper class, which wraps the Prisma client and keeps the
database token (AWS RDS IAM or Microsoft Entra ID) fresh.
é    N)ÚCallable)ÚdatetimeÚ	timedelta)ÚAnyÚFinalÚProtocol)Úverbose_proxy_logger)ÚDEFAULT_POSTGRES_PORTÚDatabaseTokenAuthÚIAMEndpointÚRdsIamTokenAuthÚmint_database_tokenÚparse_database_token_expirationÚparse_iam_endpoint_from_url)Ústr_to_bool)r   ÚPrismaManagerÚPrismaWrapperr   c                   ó   — e Zd ZU eed<   y)Ú_PrismaProcessÚpidN)Ú__name__Ú
__module__Ú__qualname__ÚobjectÚ__annotations__© ó    úc/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/db/prisma_client.pyr   r   &   s   … Ø	„Kr   r   c                   ól   — e Zd Zedefd„«       Zdededz  defd„Zdedefd„Z	deddfd„Z
deddfd	„Zy)
Ú_PrismaEngineÚreturnc                  ó   — y ©Nr   ©Úselfs    r   Úprocessz_PrismaEngine.process+   s   € Ø),r   ÚcontentÚtx_idNc             ƒ   ó   K  — y ­wr#   r   ©r%   r'   r(   s      r   Úqueryz_PrismaEngine.query.   ó   è ø� ùó   ‚c             ƒ   ó   K  — y ­wr#   r   )r%   r'   s     r   Ústart_transactionz_PrismaEngine.start_transaction0   r,   r-   c              ƒ   ó   K  — y ­wr#   r   ©r%   r(   s     r   Úcommit_transactionz _PrismaEngine.commit_transaction2   r,   r-   c              ƒ   ó   K  — y ­wr#   r   r1   s     r   Úrollback_transactionz"_PrismaEngine.rollback_transaction4   r,   r-   )r   r   r   Úpropertyr   r&   Ústrr   r+   r/   r2   r4   r   r   r   r    r    *   sR   „ ØØ,˜Ò,ó Ø,àL 3ÐL°#¸±*ÐLÀÓLàB°#ÐB¸#ÓBà?¨cÐ?°dÓ?àA°ÐA¸ÔAr   r    c                   ó.   — e Zd ZU eed<   edefd„«       Zy)Ú_PrismaClientÚ_Prisma__enginer!   c                  ó   — y r#   r   r$   s    r   Ú_enginez_PrismaClient._engine:   s   € Ø(+r   N)r   r   r   r    r   r5   r;   r   r   r   r8   r8   7   s   … Ø"Ó"àØ+˜Ò+ó Ù+r   r8   c                   óL   — e Zd Zd
d„Zd
d„Zd
d„Zdeddfd„Zdeddfd„Zd
d	„Z	y)Ú_PrismaDrainTrackerr!   Nc                 ó–   — d| _         t        «       | _        t        j                  «       | _        | j
                  j                  «        y ©Nr   )Ú_active_operationsÚ	frozensetÚ_transactionsÚasyncioÚEventÚ_drainedÚsetr$   s    r   Ú__init__z_PrismaDrainTracker.__init__?   s1   € Ø"#ˆÔÜ-6«[ˆÔÜŸ™›ˆŒØ�‰×ÑÕr   c                 ó€   — | j                   dk(  r| j                  j                  «        | xj                   dz  c_         y )Nr   é   )r@   rE   Úclearr$   s    r   Úbegin_operationz#_PrismaDrainTracker.begin_operationE   s1   € Ø×"Ñ" aÒ'Ø�M‰M×ÑÔ!Ø×Ò 1Ñ$Ör   c                 ó‚   — | xj                   dz  c_         | j                   dk(  r| j                  j                  «        y y )NrI   r   )r@   rE   rF   r$   s    r   Úend_operationz!_PrismaDrainTracker.end_operationJ   s6   € Ø×Ò 1Ñ$ÕØ×"Ñ" aÒ'Ø�M‰M×ÑÕð (r   Útransaction_idc                 óF   — | j                   j                  |f«      | _         y r#   )rB   Úunion©r%   rN   s     r   Útransaction_startedz'_PrismaDrainTracker.transaction_startedO   s   € Ø!×/Ñ/×5Ñ5°~Ð6GÓHˆÕr   c                 ó„   — || j                   vry | j                   j                  |f«      | _         | j                  «        y r#   )rB   Ú
differencerM   rQ   s     r   Útransaction_finishedz(_PrismaDrainTracker.transaction_finishedR   s;   € Ø ×!3Ñ!3Ñ3ØØ!×/Ñ/×:Ñ:¸NÐ;LÓMˆÔØ×ÑÕr   c              ƒ   óT   K  — | j                   j                  «       ƒ d {  –—†  y 7 Œ­wr#   )rE   Úwaitr$   s    r   Úwait_until_drainedz&_PrismaDrainTracker.wait_until_drainedX   s   è ø€ Ø�m‰m× Ñ Ó"×"Ò"ús   ‚( &¡(©r!   N)
r   r   r   rG   rK   rM   r6   rR   rU   rX   r   r   r   r=   r=   >   s?   „ óó%ó
 ð
I°#ð I¸$ó Ið°3ð ¸4ó ô#r   r=   c                   ó�   — e Zd Zdededdfd„Zedefd„«       Zde	de
fd„Zd	e	d
e	dz  de
fd„Zd	e	de	fd„Zd
e	ddfd„Zd
e	ddfd„Zy)Ú_TrackedPrismaEngineÚengineÚtrackerr!   Nc                 ó    — || _         || _        y r#   )r;   r]   )r%   r\   r]   s      r   rG   z_TrackedPrismaEngine.__init__]   s   € ØˆŒØˆ�r   c                 ó.   — | j                   j                  S r#   )r;   r&   r$   s    r   r&   z_TrackedPrismaEngine.processa   s   € à�|‰|×#Ñ#Ð#r   Únamec                 ó.   — t        | j                  |«      S r#   )Úgetattrr;   )r%   r`   s     r   Ú__getattr__z _TrackedPrismaEngine.__getattr__e   s   € Ü�t—|‘| TÓ*Ð*r   r'   r(   c             ƒ   ó   K  — | j                   j                  «        	 | j                  j                  ||¬«      ƒ d {  –—† | j                   j	                  «        S 7 Œ# | j                   j	                  «        w xY w­w)N)r(   )r]   rK   r;   r+   rM   r*   s      r   r+   z_TrackedPrismaEngine.queryh   s\   è ø€ Ø�‰×$Ñ$Ô&ð	)ØŸ™×+Ñ+¨G¸5Ð+ÓA×Aà�L‰L×&Ñ&Õ(ð Bùà�L‰L×&Ñ&Õ(üs,   ‚A>ž A ¾A¿A ÁA>ÁA ÁA;Á;A>c             ƒ   ó6  K  — | j                   j                  «        	 | j                  j                  |¬«      ƒ d {  –—† }| j                   j                  |«       |S 7 Œ!# t        t
        j                  f$ r | j                   j                  «        ‚ w xY w­w)N)r'   )	r]   rK   r;   r/   Ú	ExceptionrC   ÚCancelledErrorrM   rR   )r%   r'   rN   s      r   r/   z&_TrackedPrismaEngine.start_transactiono   s‚   è ø€ Ø�‰×$Ñ$Ô&ð	Ø*.¯,©,×*HÑ*HÐQXÐ*HÓ*Y×$YˆNð 	�‰×(Ñ(¨Ô8ØÐð %ZùÜœ7×1Ñ1Ð2ò 	Ø�L‰L×&Ñ&Ô(Øð	üs,   ‚BžA! ½A¾A! ÁBÁA! Á!5BÂBc              ƒ   ój  K  — | j                   j                  «        	 | j                  j                  |«      ƒ d {  –—†  | j                   j	                  «        | j                   j                  |«       y 7 Œ:# | j                   j	                  «        | j                   j                  |«       w xY w­wr#   )r]   rK   r;   r2   rM   rU   r1   s     r   r2   z'_TrackedPrismaEngine.commit_transactiony   s}   è ø€ Ø�‰×$Ñ$Ô&ð	5Ø—,‘,×1Ñ1°%Ó8×8Ð8à�L‰L×&Ñ&Ô(Ø�L‰L×-Ñ-¨eÕ4ð 9ùà�L‰L×&Ñ&Ô(Ø�L‰L×-Ñ-¨eÕ4üó,   ‚B3žA9 ¼A7½A9 Á6B3Á7A9 Á97B0Â0B3c              ƒ   ój  K  — | j                   j                  «        	 | j                  j                  |«      ƒ d {  –—†  | j                   j	                  «        | j                   j                  |«       y 7 Œ:# | j                   j	                  «        | j                   j                  |«       w xY w­wr#   )r]   rK   r;   r4   rM   rU   r1   s     r   r4   z)_TrackedPrismaEngine.rollback_transaction�   s}   è ø€ Ø�‰×$Ñ$Ô&ð	5Ø—,‘,×3Ñ3°EÓ:×:Ð:à�L‰L×&Ñ&Ô(Ø�L‰L×-Ñ-¨eÕ4ð ;ùà�L‰L×&Ñ&Ô(Ø�L‰L×-Ñ-¨eÕ4üri   )r   r   r   r    r=   rG   r5   r   r&   r6   r   rc   r+   r/   r2   r4   r   r   r   r[   r[   \   s¢   „ ð˜}ð Ð7Jð Ètó ð ð$˜ò $ó ð$ð+ ð +¨ó +ð) 3ð )°#¸±*ð )Àó )ð°#ð ¸#ó ð5¨cð 5°dó 5ð5°ð 5¸ô 5r   r[   c                   ó  — e Zd ZdZdZdZdZdZ	 dAddddd	d
œdede	de
dz  dededz  de	defd„Zede
dz  fd„«       Zedefd„«       Zede	fd„«       Zededefd„«       Zedededdfd„«       Zdededz  fd„ZdBdedz  defd„Zdededz  ddfd„Zdededz  ddfd „Zd!ej<                  d   ddfd"„ZdBd#ee z  dz  ddfd$„Z!ededdfd%„«       Z"d&edz  dedz  fd'„Z#d(edz  de$dz  fd)„Z%de&fd*„Z'd+edz  de	fd,„Z(dedz  fd-„Z)edefd.„«       Z*edefd/„«       Z+dCd0„Z,d1e&ddfd2„Z-	 dBdd3œd4ed5edz  d6edz  de	fd7„Z.	 dBdd3œd4ed5edz  d6edz  de	fd8„Z/d4eddfd9„Z0dCd:„Z1dCd;„Z2dCd<„Z3dCd=„Z4d+edz  de	fd>„Z5d?efd@„Z6y)Dr   a/  
    Wrapper around Prisma client that handles token-based database authentication.

    When a token strategy is active (AWS RDS IAM or Microsoft Entra ID), this wrapper:
    1. Proactively refreshes the token before it expires (background task)
    2. Falls back to synchronous refresh if a token is found expired
    3. Uses proper locking to prevent race conditions during reconnection

    RDS IAM tokens are valid for 15 minutes and Entra tokens for about an hour. This
    wrapper refreshes 3 minutes before whatever expiry the live token carries.
    é´   iX  é   éZ   FNÚDATABASE_URLÚ )Ú
token_authÚdb_url_env_varÚiam_endpointÚrecreate_uses_datasourceÚ
log_prefixÚoriginal_prismaÚiam_token_db_authrq   rr   rs   rt   ru   c                óX  — |�|n|r
t        «       nd | _        || _        || _        || _        || _        |r|› d�nd| _        d | _        t        j                  «       | _
        d | _        | j                  |«      | _        t        «       | _        t!        «       | _        d| _        d | _        y )Nú rp   r   )r   Ú_token_authÚ_original_prismaÚ_db_url_env_varÚ_iam_endpointÚ_recreate_uses_datasourceÚ_log_prefixÚ_token_refresh_taskrC   ÚLockÚ_reconnection_lockÚ_last_refresh_timeÚ_instrument_prisma_clientÚ_active_drain_trackerrA   Ú_retirement_tasksrF   Ú_expected_engine_deathsÚ_engine_generationÚon_engine_replaced)r%   rv   rw   rq   rr   rs   rt   ru   s           r   rG   zPrismaWrapper.__init__¥   s¬   € ð *4Ð)?™:Ñ[lÄoÔFWÐrvˆÔØ /ˆÔð  .ˆÔØ)ˆÔØ)AˆÔ&ñ
 0:˜j˜\¨Ñ+¸rˆÔð 9=ˆÔ Ü")§,¡,£.ˆÔØ37ˆÔØ%)×%CÑ%CÀOÓ%TˆÔ"Ü@IÃˆÔô 25³ˆÔ$Ø'(ˆÔØ=AˆÕr   r!   c                 ó   — | j                   S )z>The active database token strategy, or None for password auth.©rz   r$   s    r   rq   zPrismaWrapper.token_authÚ   s   € ð ×ÑÐr   c                 óJ   — | j                   �| j                   j                  S dS )z3Human name of the active token kind, for log lines.zdatabase token)rz   Úlabelr$   s    r   Útoken_labelzPrismaWrapper.token_labelß   s'   € ð *.×)9Ñ)9Ð)Eˆt×Ñ×%Ñ%Ð[ÐK[Ð[r   c                 ó   — | j                   duS )zÓWhether any token strategy is active.

        Read-only: the kind of token is chosen once, by injection, so there is no way
        to flip this back on and silently get AWS RDS on an Azure deployment.
        Nr‹   r$   s    r   rw   zPrismaWrapper.iam_token_db_authä   s   € ð ×Ñ tÐ+Ð+r   Úprisma_clientc                 ó   — | j                   S r#   )r;   )r�   s    r   Ú_read_enginezPrismaWrapper._read_engineí   s   € à×$Ñ$Ð$r   r\   c                 ó   — || _         y r#   )r9   )r�   r\   s     r   Ú_write_enginezPrismaWrapper._write_engineñ   s
   € à(.ˆÕ%r   c                 óÜ   — ddl m} 	 | j                  |«      }t	        |t
        «      r|j                  S t        «       }| j                  |t        ||«      «       |S # t        |f$ r Y y w xY w)Nr   ©ÚClientNotConnectedError)	Úprisma.errorsr—   r’   ÚAttributeErrorÚ
isinstancer[   r]   r=   r”   )r%   r�   r—   r\   r]   s        r   r„   z'PrismaWrapper._instrument_prisma_clientõ   sp   € Ý9ð	Ø ×-Ñ-¨mÓ<ˆFô �fÔ2Ô3Ø—>‘>Ð!Ü,Ó.ˆØ×Ñ˜=Ô*>¸vÀwÓ*OÔPØˆøô Ð 7Ð8ò 	Ùð	ús   ˆA ÁA+Á*A+c                 óÎ   — ddl m} 	 |€| j                  n|}| j                  |«      j                  j
                  }t        |t        «      r|S 	 y# t        |t        f$ r Y yw xY w)aÕ  Get the PID of the current Prisma engine subprocess, or 0 if unavailable.

        Must never raise: it runs inside the reconnect path, where the client
        may be in any broken state. Prisma's ``_engine`` is a property that
        raises ``ClientNotConnectedError`` on a disconnected client; if that
        escaped here, ``recreate_prisma_client`` would fail before it could
        build a replacement client and the reconnect loop could never recover.
        r   r–   )
r˜   r—   r{   r’   r&   r   rš   Úintr™   Ú	TypeError)r%   r�   r—   Útarget_prismar   s        r   Ú_get_engine_pidzPrismaWrapper._get_engine_pid  ss   € õ 	:ð	Ø<IÐ<Q 4×#8Ò#8ÐWdˆMØ×*Ñ*¨=Ó9×AÑA×EÑEˆCÜ˜#œsÔ#Ø�
ð $ð øô Ð 7¼ÐCò 	ØØð	ús   ˆAA ÁA$Á#A$r   r]   c              ƒ   óD  K  — |�8	 t        j                  |j                  «       | j                  ¬«      ƒ d {  –—†  | j                  |«      ƒ d {  –—†  y 7 Œ# t         j                  $ r/ t        j                  d| j                  || j                  «       Y Œ]w xY w7 ŒL­w)N©Útimeoutz_%sReplaced prisma engine PID %s did not drain within %ss; killing it with work still in flight.)	rC   Úwait_forrX   Ú'ENGINE_RETIREMENT_DRAIN_TIMEOUT_SECONDSÚTimeoutErrorr	   Úwarningr   Ú_kill_engine_process)r%   r   r]   s      r   Ú_retire_engine_when_drainedz)PrismaWrapper._retire_engine_when_drained  s›   è ø€ ØÐðÜ×&Ñ&Ø×.Ñ.Ó0Ø ×HÑHô÷ ð ð ×'Ñ'¨Ó,×,Ñ,ðùô ×'Ñ'ò Ü$×,Ñ,ØuØ×$Ñ$ØØ×@Ñ@ö	ðúð 	-úsC   ‚B †2A ¸A¹A ½B ÁBÁB ÁA Á?BÂB ÂBÂB c                 óÒ   — |dk  ry t        j                  | j                  ||«      «      }| j                  j	                  |f«      | _        |j                  | j                  «       y r?   )rC   Úcreate_taskr¨   r†   rP   Úadd_done_callbackÚ_retirement_finished)r%   r   r]   Úretirement_tasks       r   Ú_schedule_engine_retirementz)PrismaWrapper._schedule_engine_retirement&  s[   € Ø�!Š8ØÜ!(×!4Ñ!4°T×5UÑ5UÐVYÐ[bÓ5cÓ!dˆØ!%×!7Ñ!7×!=Ñ!=¸Ð>PÓ!QˆÔØ×)Ñ)¨$×*CÑ*CÕDr   r­   c                 óF   — | j                   j                  |f«      | _         y r#   )r†   rT   )r%   r­   s     r   r¬   z"PrismaWrapper._retirement_finished-  s   € Ø!%×!7Ñ!7×!BÑ!BÀOÐCUÓ!VˆÕr   r¢   c              ƒ   óä   K  — |€#| j                   j                  «       ƒ d {  –—†  n#| j                   j                  |«      ƒ d {  –—†  | j                  | j                   «      | _        y 7 ŒI7 Œ'­wr#   )r{   Úconnectr„   r…   )r%   r¢   s     r   r±   zPrismaWrapper.connect0  s_   è ø€ Øˆ?Ø×'Ñ'×/Ñ/Ó1×1Ñ1à×'Ñ'×/Ñ/°Ó8×8Ð8Ø%)×%CÑ%CÀD×DYÑDYÓ%ZˆÕ"ð 2øà8ús!   ‚ A0¢A,£#A0ÁA.Á&A0Á.A0c              ƒ   óÈ  K  — | dk  ry	 t        j                  | t        j                  «       t        j                  d| «       t        j                  d«      ƒ d{  –—†  	 t        j                  | t        t        dt        j                  «      «       t        j                  d| «       y# t        t
        t        f$ r Y yw xY w7 Œi# t        t
        t        f$ r Y yw xY w­w)uù  Force-kill the engine subprocess to prevent DB connection pool leaks.

        Called on every reconnect (in `recreate_prisma_client`) to retire the
        old query-engine subprocess without invoking prisma-client-py's
        synchronous `disconnect()` â€” which blocks the asyncio event loop on
        `subprocess.Popen.wait()` for 30-120+ seconds when the engine is
        stuck on TCP close.

        Sends SIGTERM for graceful shutdown, waits briefly, then SIGKILL as
        a backstop.
        r   Nz<Sent SIGTERM to prisma-query-engine PID %s during reconnect.g      à?ÚSIGKILLzHSent SIGKILL to prisma-query-engine PID %s (did not exit after SIGTERM).)ÚosÚkillÚsignalÚSIGTERMÚProcessLookupErrorÚPermissionErrorÚOSErrorr	   r¦   rC   Úsleeprb   )r   s    r   r§   z"PrismaWrapper._kill_engine_process7  sÁ   è ø€ ð �!Š8Øð	Ü�G‰G�CœŸ™Ô(ô 	×$Ñ$ØJØô	
ô
 �m‰m˜CÓ × Ð ð	Ü�G‰G�Cœ¤¨´F·N±NÓCÔDÜ ×(Ñ(ØZØõøô #¤O´WÐ=ò 	Ùð	úð 	!ùô #¤O´WÐ=ò 	Ùð	üsX   ‚C"Š$B, ®.C"ÁCÁC"Á"A	C Â+C"Â,CÃ C"ÃCÃC"ÃCÃC"ÃCÃC"Údb_urlc                 óÒ   — |€y	 t         j                  j                  |«      }|j                  r)t         j                  j	                  |j                  «      S y# t
        $ r Y yw xY w)aX  
        Extract the token (password) from the DATABASE_URL.

        The token contains the AWS signature with X-Amz-Date and X-Amz-Expires parameters.

        Important: We must parse the URL while it's still encoded to preserve structure,
        then decode the password portion. Otherwise the '?' in the token breaks URL parsing.
        N)ÚurllibÚparseÚurlparseÚpasswordÚunquoterf   )r%   r¼   Úparseds      r   Ú_extract_token_from_db_urlz(PrismaWrapper._extract_token_from_db_urlY  sZ   € ð ˆ>Øð	ä"ŸL™L×1Ñ1°&Ó9ˆFØ�Šä—|‘|×+Ñ+¨F¯O©OÓ<Ð<ØøÜò 	Ùð	ús   …AA Á	A&Á%A&Útokenc                 óL   — |�| j                   €yt        | j                   |«      S )z‘
        Parse the token to extract its expiration time.

        Returns the datetime when the token expires, or None if parsing fails.
        N)rz   r   )r%   rÅ   s     r   Ú_parse_token_expirationz%PrismaWrapper._parse_token_expirationn  s*   € ð ˆ=˜D×,Ñ,Ð4ØÜ.¨t×/?Ñ/?ÀÓGÐGr   c                 óŒ  — t        j                  | j                  «      }| j                  |«      }| j	                  |«      }|€,t        j                  d| j                  «       | j                  S |t        | j                  ¬«      z
  }t        j                  «       }||z
  j                  «       }t        | j                  |«      S )a  
        Calculate exactly how many seconds until we need to refresh the token.

        Uses precise timing: sleeps until (token_expiration - buffer_seconds).
        For a 15-minute (900s) token with 180s buffer, this returns ~720s (12 min).

        Returns:
            Number of seconds to sleep before the next refresh, never less than
            TOKEN_REFRESH_MIN_SLEEP_SECONDS so a token whose expiry never advances
            cannot spin the loop.
            Returns FALLBACK_REFRESH_INTERVAL_SECONDS if parsing fails.
        z@Could not parse token expiration, using fallback interval of %ss)Úseconds)r´   Úgetenvr|   rÄ   rÇ   r	   ÚdebugÚ!FALLBACK_REFRESH_INTERVAL_SECONDSr   ÚTOKEN_REFRESH_BUFFER_SECONDSr   ÚutcnowÚtotal_secondsÚmaxÚTOKEN_REFRESH_MIN_SLEEP_SECONDS)r%   r¼   rÅ   Úexpiration_timeÚ
refresh_atÚnowÚseconds_until_refreshs          r   Ú _calculate_seconds_until_refreshz.PrismaWrapper._calculate_seconds_until_refreshx  sµ   € ô Ÿ	™	 $×"6Ñ"6Ó7ˆØ×6Ñ6°vÓ>ˆØ!%×!=Ñ!=¸eÓ!DˆàÐ"ä ×&Ñ&ØRØ×6Ñ6ôð ×9Ñ9Ð9ð ,¬iÀ×@aÑ@aÔ.bÑbˆ
ô —_‘_Ó&ˆØ(2°SÑ(8×'GÑ'GÓ'IÐô
 �4×7Ñ7Ð9NÓOÐOr   Ú	token_urlc                 óª   — |€y| j                  |«      }| j                  |«      }|€t        j                  d«       yt	        j
                  «       |kD  S )z/Check if the token in the given URL is expired.Tz5Could not parse token expiration, treating as expired)rÄ   rÇ   r	   rË   r   rÎ   )r%   r×   rÅ   rÒ   s       r   Úis_token_expiredzPrismaWrapper.is_token_expired�  sU   € àÐØà×6Ñ6°yÓAˆØ!%×!=Ñ!=¸eÓ!DˆàÐ"ä ×&Ñ&Ð'^Ô_Øä�‰Ó  ?Ñ2Ð2r   c                 óä   — | j                   }|€y| j                  �| j                  n| j                  «       }|j                  t	        ||«      «      }|t
        j                  | j                  <   |S )ab  Mint a fresh database token and update the configured DB URL env var.

        When the wrapper was constructed with an explicit `iam_endpoint`
        (typical for a reader wrapper whose host/port/user came from a parsed
        URL), use that. Otherwise fall back to the DATABASE_HOST/PORT/USER/
        NAME/SCHEMA env vars (writer behavior).
        N)rz   r}   Ú_endpoint_from_envÚ	build_urlr   r´   Úenvironr|   )r%   ÚauthÚendpointr¼   s       r   Úget_rds_iam_tokenzPrismaWrapper.get_rds_iam_token¬  sj   € ð ×&Ñ&ˆØˆ<Øà04×0BÑ0BÐ0N˜$×,Ò,ÐTX×TkÑTkÓTmˆØ ×*Ñ*Ô+>¸tÀXÓ+NÓOˆØ+1Œ�
‰
�4×'Ñ'Ñ(Øˆr   c            	      ót  — t        j                  d«      } t        j                  d«      }t        j                  d«      }| r|r|s9t        d„ d| fd|fd|ffD «       «      }t        ddj	                  |«      › d�«      ‚t        | t        j                  dt        «      ||t        j                  d	«      ¬
«      S )NÚDATABASE_HOSTÚDATABASE_USERÚDATABASE_NAMEc              3   ó*   K  — | ]  \  }}|s|–— Œ y ­wr#   r   )Ú.0ÚenvÚvalues      r   ú	<genexpr>z3PrismaWrapper._endpoint_from_env.<locals>.<genexpr>Ã  s   è ø€ ò #á�C˜Ùô ñ#ùs   ‚zCannot mint a database token: z, zX unset. Set them so the connection URL can be reassembled around a freshly minted token.ÚDATABASE_PORTÚDATABASE_SCHEMA)ÚhostÚportÚuserr`   Úschema)r´   rÊ   ÚtupleÚRuntimeErrorÚjoinr   r
   )rì   rî   r`   Úmissings       r   rÛ   z PrismaWrapper._endpoint_from_env½  sÅ   € ä—i‘i Ó0ˆÜ—i‘i Ó0ˆÜ—i‘i Ó0ˆÙ™4¡tÜ"ñ #à$3°TÐ#:¸_ÈdÐ<SÐVeÐgkÐUlÐ"mô#ó ˆGô
 Ø0°·±¸7Ó1CÐ0Dð ESð Sóð ô Øô —‘˜?Ô,AÓBØØÜ—9‘9Ð.Ó/ô	
ð 		
r   c                 ó   — | j                   S )uZ  How many query-engine replacements have completed on this wrapper.

        Bumped under `_reconnection_lock` only after a replacement engine has
        connected, so a change across an await proves a *successful* planned
        replacement happened in between â€” a replacement that failed (a real
        outage) leaves it untouched.
        )rˆ   r$   s    r   Úengine_generationzPrismaWrapper.engine_generation×  s   € ð ×&Ñ&Ð&r   c              ƒ   óŠ   K  — | j                   4 ƒd {  –—†  	 d d d «      ƒd {  –—†  y 7 Œ7 Œ# 1 ƒd {  –—†7  sw Y   y xY w­wr#   )r‚   r$   s    r   Ú_reconnection_settledz#PrismaWrapper._reconnection_settledâ  sA   è ø€ Ø×*Ñ*÷ 	ñ 	Ø÷	÷ 	ñ 	øð 	ø÷ 	÷ 	ñ 	üs8   ‚A“*”A—.™A¤,¥A¬A®A ´7µA ¼AÚtimeout_secondsc              ƒ   óÞ   K  — |dk  s| j                   j                  «       sy	 t        j                  | j	                  «       |¬«      ƒ d{  –—†  y7 Œ# t        j
                  $ r Y yw xY w­w)aü  Wait, bounded, for an in-flight planned engine replacement to finish.

        Both replacement paths (`recreate_prisma_client` and
        `_safe_refresh_token`) hold `_reconnection_lock` across their whole
        kill/connect window, so re-acquiring it means the replacement has
        settled one way or the other. Gives up silently on timeout: a caller
        that stopped waiting must treat the replacement as not completed and
        consult `engine_generation` rather than assume success.
        r   Nr¡   )r‚   ÚlockedrC   r£   r÷   r¥   )r%   rø   s     r   Ú#wait_for_planned_engine_replacementz1PrismaWrapper.wait_for_planned_engine_replacementæ  s_   è ø€ ð ˜aÒ t×'>Ñ'>×'EÑ'EÔ'GØð	Ü×"Ñ" 4×#=Ñ#=Ó#?ÈÔY×YÒYùÜ×#Ñ#ò 	Ùð	üs:   ‚!A-¤(A ÁAÁA ÁA-ÁA ÁA*Á'A-Á)A*Á*A-)Úexpected_generationÚ
new_db_urlÚhttp_clientrü   c             ƒ   óÄ   K  — | j                   4 ƒd{  –—†  | j                  |||¬«      ƒ d{  –—† cddd«      ƒd{  –—†  S 7 Œ17 Œ7 Œ	# 1 ƒd{  –—†7  sw Y   yxY w­w)u{  Disconnect and reconnect the Prisma client with a new database URL.

        Kills the old engine subprocess directly (SIGTERM â†’ SIGKILL) rather than
        calling `disconnect()`. prisma-client-py's `disconnect()` calls a
        synchronous `subprocess.Popen.wait()` that can freeze the asyncio event
        loop for 30-120+ seconds when the engine is stuck on TCP close,
        breaking `/health/liveliness` and causing Kubernetes pod restarts.

        The writer wrapper relies on Prisma re-reading `DATABASE_URL` from env;
        the reader wrapper opts into `recreate_uses_datasource=True` so the
        new URL is passed explicitly via `datasource={"url": ...}` (Prisma
        does not auto-read alternate env vars like DATABASE_URL_READ_REPLICA).

        Serializes all recreations through `self._reconnection_lock` so the
        IAM-refresh path and the engine-death/transport-error reconnect paths
        cannot recreate concurrently (issue #29176). `expected_generation`, if
        given, is an optimistic-lock token: when it no longer matches
        `self._engine_generation` once the lock is held, another path already
        replaced the engine, so this call is a no-op and returns ``False``.

        Returns:
            bool: ``True`` if the client was actually recreated, ``False`` if
            the recreate was skipped because the engine generation moved on.
        N)rþ   rü   )r‚   Ú_recreate_prisma_client_locked)r%   rý   rþ   rü   s       r   Úrecreate_prisma_clientz$PrismaWrapper.recreate_prisma_client÷  sg   è ø€ ð> ×*Ñ*÷ 	ñ 	Ø×<Ñ<ØØ'Ø$7ð =ó ÷ ÷	÷ 	ò 	øðøð	ø÷ 	÷ 	ñ 	üsR   ‚A “A”A —A¯A°A³A ¿A	Á A ÁAÁ	A ÁAÁAÁAÁA c             ƒ   óÊ  K  — ddl m} |�<|| j                  k7  r-t        j                  d| j
                  | j                  |«       y| j                  «       }|dkD  rft        | j                  «      dk\  r| j                  j                  «        | j                  j                  |«       | j                  |«      ƒ d{  –—†  i }|�||d<   | j                  rd|i|d	<    |di |¤Ž| _        | j                  j                  «       ƒ d{  –—†  | j                  | j                  «      | _        | xj                  d
z  c_        | j"                  �| j#                  «        y7 Œ¡7 ŒX­w)u  Core recreate logic. Caller MUST hold `self._reconnection_lock`.

        Split out so callers that already hold the lock (e.g.
        `_safe_refresh_token`, which double-checks token freshness under the
        lock) don't re-acquire it â€” `asyncio.Lock` is not reentrant.
        r   ©ÚPrismaNzZ%sSkipping Prisma client recreate: engine already replaced (generation %s != expected %s).Fé@   ÚhttpÚurlÚ
datasourcerI   Tr   )Úprismar  rˆ   r	   Úinfor   rŸ   Úlenr‡   rJ   Úaddr§   r~   r{   r±   r„   r…   r‰   )r%   rý   rþ   rü   r  Úold_engine_pidÚkwargss          r   r   z,PrismaWrapper._recreate_prisma_client_locked  sU  è ø€ õ 	"àÐ*Ð/BÀd×F]ÑF]Ò/]Ü ×%Ñ%ØlØ× Ñ Ø×'Ñ'Ø#ô	ð à $× 4Ñ 4Ó 6ˆØ˜AÒô �4×/Ñ/Ó0°BÒ6Ø×,Ñ,×2Ñ2Ô4Ø×(Ñ(×,Ñ,¨^Ô<Ø×+Ñ+¨NÓ;×;Ð;à(*ˆØÐ"Ø(ˆF�6‰NØ×)Ò)Ø$)¨:Ð#6ˆF�<Ñ Ù &Ñ 0¨Ñ 0ˆÔà×#Ñ#×+Ñ+Ó-×-Ð-Ø%)×%CÑ%CÀD×DYÑDYÓ%ZˆÔ"Ø×Ò 1Ñ$Õð
 ×"Ñ"Ð.Ø×#Ñ#Ô%àð' <øð 	.ús&   ‚B;E#Â=EÂ>A
E#ÄE!Ä	AE#Å!E#c              ƒ   ó  K  — ddl m} ddlm} | j                  r ||¬«      } ||¬«      }n |«       }| j
                  }| j                  |«      }| j                  }|€| j                  |«      }	 |j                  «       ƒ d {  –—†  | j                  |«      }	|dkD  rMt        | j                  «      dk\  r| j                  j!                  «        | j                  j#                  |«       || _        |	| _        | xj$                  dz  c_        | j&                  �| j'                  «        | j                  ||«       y 7 Œ¹# t        t        j                  f$ r# | j                  | j                  |«      d «       ‚ w xY w­w)Nr   r  )ÚDatasourceOverride)r  )r  r  rI   )r	  r  Úprisma.typesr  r~   r{   rŸ   r…   r„   r±   rf   rC   rg   r®   r  r‡   rJ   r  rˆ   r‰   )
r%   rý   r  r  r  Úreplacement_prismaÚ
old_prismar  Úold_drain_trackerÚreplacement_drain_trackers
             r   Ú/_replace_prisma_client_for_token_refresh_lockedz=PrismaWrapper._replace_prisma_client_for_token_refresh_lockedX  sf  è ø€ õ 	"Ý3à×)Ò)Ù 2°zÔ BˆJÙ!'°:Ô!>Ñá!'£Ðà ×1Ñ1ˆ
Ø $× 4Ñ 4°ZÓ @ˆØ ×6Ñ6ÐØÐ$Ø $× >Ñ >¸zÓ JÐð	Ø$×,Ñ,Ó.×.Ð.ð ,0×+IÑ+IÐJ\Ó+]Ð!à˜AÒÜ�4×/Ñ/Ó0°BÒ6Ø×,Ñ,×2Ñ2Ô4Ø×(Ñ(×,Ñ,¨^Ô<à 2ˆÔØ%>ˆÔ"Ø×Ò 1Ñ$Õà×"Ñ"Ð.Ø×#Ñ#Ô%à×(Ñ(¨Ð9JÕKð% /ùÜœ7×1Ñ1Ð2ò 	Ø×,Ñ,¨T×-AÑ-AÐBTÓ-UÐW[Ô\Øð	üs1   ‚A/F Á2E ÂD?ÂE Â
B5F Ä?E Å<E=Å=F c              ƒ   ó:  K  — | j                   st        j                  d«       y| j                  �t        j                  d«       yt	        j
                  | j                  «       «      | _        t        j                  d| j                  | j                  «       y­w)a  
        Start the background token refresh task.

        This task proactively refreshes the database token before it expires,
        preventing connection failures. Should be called after the initial
        Prisma client connection is established.
        z<Database token auth not enabled, skipping token refresh taskNz"Token refresh task already runningz.%sStarted %s proactive refresh background task)
rw   r	   rË   r€   rC   rª   Ú_token_refresh_loopr
  r   rŽ   r$   s    r   Ústart_token_refresh_taskz&PrismaWrapper.start_token_refresh_task  s€   è ø€ ð ×%Ò%Ü ×&Ñ&Ð'eÔfØà×#Ñ#Ð/Ü ×&Ñ&Ð'KÔLØä#*×#6Ñ#6°t×7OÑ7OÓ7QÓ#RˆÔ Ü×!Ñ!Ø<Ø×ÑØ×Ñõ	
ùs   ‚BBc              ƒ   ó  K  — | j                   €y| j                   j                  «        	 | j                   ƒ d{  –—†  d| _         t	        j
                  d| j                  | j                  «       y7 Œ7# t        j                  $ r Y ŒJw xY w­w)z‘
        Stop the background token refresh task gracefully.

        Should be called during application shutdown to clean up resources.
        Nz$%sStopped %s refresh background task)r€   ÚcancelrC   rg   r	   r
  r   rŽ   r$   s    r   Ústop_token_refresh_taskz%PrismaWrapper.stop_token_refresh_task–  s†   è ø€ ð ×#Ñ#Ð+Øà× Ñ ×'Ñ'Ô)ð	Ø×*Ñ*×*Ð*ð $(ˆÔ Ü×!Ñ!Ø2Ø×ÑØ×Ñõ	
ð	 +ùÜ×%Ñ%ò 	Ùð	üs7   ‚(B«A4 ºA2»A4 ¿3BÁ2A4 Á4B
ÂBÂ	B
Â
Bc           	   ƒ   ó†  K  — t        j                  d| j                  | j                  | j                  «       	 	 | j                  «       }|dkD  rVt        j                  | j                  › | j                  › d|d›d|dz  d›d�«       t        j                  |«      ƒ d	{  –—†  t        j                  d
| j                  | j                  «       | j                  «       ƒ d	{  –—†  Œ°7 ŒH7 Œ# t        j                  $ r. t        j                  d| j                  | j                  «       Y y	t        $ rˆ}t        j                  d| j                  | j                  || j                  «       	 t        j                  | j                  «      ƒ d	{  –—†7   n# t        j                  $ r Y Y d	}~y	w xY wY d	}~ŒÐd	}~ww xY w­w)aV  
        Background loop that proactively refreshes database tokens before expiration.

        Uses precise timing: calculates the exact sleep duration until the token
        needs to be refreshed (expiration - 3 minute buffer), then refreshes.
        This is more efficient than polling, requiring only 1 wake-up per token cycle.
        zJ%s%s refresh loop started. Tokens will be refreshed %ss before expiration.r   z refresh scheduled in z.0fz
 seconds (é<   z.1fz	 minutes)Nz%sProactively refreshing %s...z%s%s refresh loop cancelledz2%sError in %s refresh loop: %s. Retrying in %ss...)r	   r
  r   rŽ   rÍ   rÖ   rC   r»   Ú_safe_refresh_tokenrg   rf   ÚerrorrÌ   )r%   Úsleep_secondsÚes      r   r  z!PrismaWrapper._token_refresh_loop«  s¦  è ø€ ô 	×!Ñ!ØXØ×ÑØ×ÑØ×-Ñ-ô		
ð ð&à $× EÑ EÓ G�à  1Ò$Ü(×-Ñ-Ø×+Ñ+Ð,¨T×-=Ñ-=Ð,>Ð>TØ(¨Ð-¨Z¸ÈÑ8JÈ3Ð7OÈyðZôô "Ÿ-™-¨Ó6×6Ð6ô %×)Ñ)Ø4Ø×$Ñ$Ø×$Ñ$ôð
 ×.Ñ.Ó0×0Ð0ð% ð 7øð 1ùä×)Ñ)ò Ü$×)Ñ)Ø1Ø×$Ñ$Ø×$Ñ$ôñ
 Üò Ü$×*Ñ*ØHØ×$Ñ$Ø×$Ñ$ØØ×:Ñ:ôðÜ!Ÿ-™-¨×(NÑ(NÓO×OÒOøÜ×-Ñ-ò Ýðúô Pûðüs–   ‚8G»A&C. Â!C*Â"AC. Ã$C,Ã%C. Ã)GÃ*C. Ã,C. Ã.>F>Ä,GÄ.F>Ä67F9Å."FÆFÆFÆF9ÆF1Æ*F9Æ+GÆ0F1Æ1F9Æ4GÆ9F>Æ>Gc              ƒ   ó®  K  — | j                   4 ƒd{  –—†  | j                  t        j                  | j                  «      «      r=t        j                  d| j                  | j                  «       	 ddd«      ƒd{  –—†  yt        j                  | j                  «      }| j                  «       }|r_	 | j                  |«      ƒ d{  –—†  t!        j"                  «       | _        t        j&                  d| j                  | j                  «       n+t        j(                  d| j                  | j                  «       ddd«      ƒd{  –—†  y7 �Œ<7 ŒÔ7 ŒŠ# t        t        j                  f$ rL |€+t        j                  j                  | j                  d«       ‚ |t        j                  | j                  <   ‚ w xY w7 Œt# 1 ƒd{  –—†7  sw Y   yxY w­w)zð
        Refresh the database token with proper locking to prevent race conditions.

        Uses an asyncio lock to ensure only one refresh operation happens at a time,
        preventing multiple concurrent reconnection attempts.
        Nz-%s%s still fresh; skipping redundant refresh.z%s%s refreshed successfully.z4%sFailed to generate new %s during proactive refresh)r‚   Ú_token_refresh_not_neededr´   rÊ   r|   r	   rË   r   rŽ   rà   r  rf   rC   rg   rÝ   Úpopr   rÎ   rƒ   r
  r   )r%   Úprevious_db_urlrý   s      r   r  z!PrismaWrapper._safe_refresh_tokenã  s“  è ø€ ð ×*Ñ*÷ $	ñ $	ð ×-Ñ-¬b¯i©i¸×8LÑ8LÓ.MÔNÜ$×*Ñ*ØCØ×$Ñ$Ø×$Ñ$ôð
 ÷$	÷ $	ð $	ô &(§Y¡Y¨t×/CÑ/CÓ%DˆOØ $× 6Ñ 6Ó 8ˆJÙðØ×NÑNÈzÓZ×ZÐZô +3¯/©/Ó*;�Ô'Ü$×)Ñ)Ø2Ø×$Ñ$Ø×$Ñ$õô %×*Ñ*ØJØ×$Ñ$Ø×$Ñ$ô÷A$	÷ $	ñ $	ùð $	øð$ [ùÜ!¤7×#9Ñ#9Ð:ò Ø&Ð.ÜŸ
™
Ÿ™ t×';Ñ';¸TÔBð ð <KœŸ
™
 4×#7Ñ#7Ñ8Øðúð'$	ø÷ $	÷ $	ñ $	üs�   ‚G“E”G—AG Á2GÁ=EÁ>GÂ1G Â5EÃ	EÃ
EÃA0G Ä>GÅ	F>Å
GÅGÅEÅA%F;Æ;G Æ>GÇ GÇG	ÇGÇGc                 ó´   — | j                  |«      }| j                  |«      }|€y|t        j                  «       z
  j	                  «       }|| j
                  kD  S )u  True iff the token in ``token_url`` has more than the refresh buffer
        of runway left, so a refresh would be redundant.

        Used to coalesce stacked refresh triggers. Deliberately mirrors the
        proactive loop's schedule (refresh at ``expiration - buffer``): a token
        with exactly ``buffer`` seconds left is NOT considered fresh, so the
        legitimate proactive refresh still fires. Unparseable tokens return
        ``False`` (refresh) â€” skipping them would mean never refreshing.
        F)rÄ   rÇ   r   rÎ   rÏ   rÍ   )r%   r×   rÅ   rÒ   Úseconds_lefts        r   r$  z'PrismaWrapper._token_refresh_not_needed  sX   € ð ×6Ñ6°yÓAˆØ!%×!=Ñ!=¸eÓ!DˆØÐ"ØØ.´·±Ó1BÑB×QÑQÓSˆØ˜d×?Ñ?Ñ?Ð?r   r`   c                 óú  — t        | j                  |«      }| j                  �rEt        j                  | j
                  «      }| j                  |«      �r	 t        j                  «       }|�Lt        j                  d| j                  | j                  «       |j                  | j                  «       «       |S t        j                  d| j                  | j                  «       | j!                  «       }|r\t        j"                  | j%                  |«      «       t        | j                  |«      }t        j&                  d| j                  «       |S t)        d| j                  › �«      ‚|S # t        $ r d}Y �Œw xY w)u  
        Proxy attribute access to the underlying Prisma client.

        If IAM token auth is enabled and the token is found expired here, the
        proactive refresh task has missed its window. Behavior depends on
        whether we're called from inside a running event loop:

        - Inside the loop (typical: from a coroutine): schedule a refresh as a
          background task and return the (stale) attribute. The caller's await
          will likely fail with a connection error and be retried by upper
          layers (`call_with_db_reconnect_retry`); by that time the refresh
          has either completed or escalated to the proactive loop's error
          path. We CANNOT block here â€” `run_coroutine_threadsafe(...)` +
          `future.result()` from inside the same loop deadlocks the loop
          (loop thread is blocked, scheduled coroutine never runs, 30s timeout).

        - No running loop (sync caller, mostly tests): run the refresh in a
          fresh loop and re-fetch the attribute.
        Nzœ%s%s expired in __getattr__ - proactive refresh may have failed. Scheduling async refresh; the current request may fail and be retried with the fresh token.zk%s%s expired in __getattr__ - proactive refresh may have failed. Triggering synchronous fallback refresh...z2%sSynchronous token refresh completed successfullyzFailed to get )rb   r{   rw   r´   rÊ   r|   rÙ   rC   Úget_running_looprñ   r	   r¦   r   rŽ   rª   r  rà   Úrunr  r
  Ú
ValueError)r%   r`   Úoriginal_attrr¼   Úrunning_looprý   s         r   rc   zPrismaWrapper.__getattr__!  se  € ô(   × 5Ñ 5°tÓ<ˆà×!Ó!ÜŸI™I d×&:Ñ&:Ó;ˆFð ×$Ñ$ VÕ,ð(Ü#*×#;Ñ#;Ó#=�Lð  Ð+Ü(×0Ñ0ðPð ×(Ñ(Ø×(Ñ(ôð !×,Ñ,¨T×-EÑ-EÓ-GÔHð( Ðô% )×0Ñ0ðVà×(Ñ(Ø×(Ñ(ô	ð )-×(>Ñ(>Ó(@�JÙ!ÜŸ™ D×$?Ñ$?À
Ó$KÔLä(/°×0EÑ0EÀtÓ(L˜Ü,×1Ñ1ØPØ ×,Ñ,ôð Ðô )¨>¸$×:JÑ:JÐ9KÐ)LÓMÐMàÐøôE $ò (Ø#'“Lð(ús   ÁE+ Å+E:Å9E:)Fr#   rY   )7r   r   r   Ú__doc__rÍ   rÌ   rÑ   r¤   r   Úboolr   r6   r   rG   r5   rq   rŽ   rw   Ústaticmethodr8   r    r’   r”   r=   r„   rœ   rŸ   r¨   r®   rC   ÚTaskr¬   r   r±   r§   rÄ   r   rÇ   ÚfloatrÖ   rÙ   rà   rÛ   rõ   r÷   rû   r  r   r  r  r  r  r  r$  rc   r   r   r   r   r   Š   s¸  „ ñ
ð $'Ð ð ),Ð%ð
 ')Ð#à.0Ð+ð
 #(ð3Bð
 04Ø,Ø+/Ø).Øò3Bàð3Bð  ð3Bð
 &¨Ñ,ð3Bð ð3Bð " DÑ(ð3Bð #'ð3Bð ó3Bðj ð Ð-°Ñ4ò  ó ð ð ð\˜Sò \ó ð\ð ð, 4ò ,ó ð,ð ð% Mð %°mò %ó ð%ð ð/ ]ð /¸Mð /Èdò /ó ð/ð°}ð ÐI\Ð_cÑIcó ñ¨]¸TÑ-Að ÈSó ð(-°Sð -ÐCVÐY]ÑC]ð -Ðbfó -ð E¨sð EÐ=PÐSWÑ=Wð EÐ\`ó EðW°G·L±LÀÑ4Fð WÈ4ó Wñ[ S¨9¡_°tÑ%;ð [Àtó [ð ð¨ð °ò ó ððB°°t±ð ÀÀdÁ
ó ð*H¨S°4©Zð H¸HÀt¹Oó Hð#P°%ó #PðJ3¨#°©*ð 3¸ó 3ð 3¨¡:ó ð" ð
 ò 
ó ð
ð2 ð' 3ò 'ó ð'óðÈð ÐSWó ð( #'ð$ð
 +/ò$àð$ð ˜4‘Zð$ð
 ! 4™Zð$ð 
ó$ðR #'ð9ð
 +/ò9àð9ð ˜4‘Zð9ð
 ! 4™Zð9ð 
ó9ðv%Làð%Lð 
ó%LóN
ó.
ó*6óp+ðZ@°3¸±:ð @À$ó @ð"? ô ?r   r   c                   óf   — e Zd Zedefd„«       Zed	d„«       Zed	d„«       Zed
dededefd„«       Z	y)r   r!   c                  óÄ   — t         j                  j                  t        «      } t         j                  j	                  t         j                  j	                  | «      «      }|S )z(Get the path to the migrations directory)r´   ÚpathÚabspathÚ__file__Údirname)r7  Údnames     r   Ú_get_prisma_dirzPrismaManager._get_prisma_dird  s:   € ô Ÿ™Ÿ™¬Ó2ˆÜ—w‘w—‘¤r§w¡w§¡°wÓ'?Ó@ˆØˆr   Nc                  óP   — 	 ddl m}  | j                  «        y# t        $ r Y yw xY w)zö
        `prisma db push` bypasses litellm-proxy-extras, so the opt-in
        REPLICA IDENTITY FULL step has to be driven from here too.

        litellm-proxy-extras is an optional install, so this is a no-op when it
        is absent.
        r   ©ÚProxyExtrasDBManagerN)Úlitellm_proxy_extras.utilsr>  ÚImportErrorÚ(apply_replica_identity_full_if_requestedr=  s    r   Ú)_apply_replica_identity_full_if_requestedz7PrismaManager._apply_replica_identity_full_if_requestedk  s,   € ð	ÝGð 	×EÑEÕGøô ò 	Ùð	ús   ‚ ™	%¤%c                  ój   — 	 ddl m} m} |j	                  «       rt        | «      ‚y# t        $ r Y yw xY w)zý`prisma db push` rewrites a doc-partitioned LiteLLM_SpendLogs
        primary key back to ("request_id"), which Postgres rejects. Fail fast
        with guidance instead of retrying into that raw error. No-op when
        litellm-proxy-extras is absent.r   ©Ú!PARTITIONED_SPEND_LOGS_PUSH_ERRORr>  N)r?  rE  r>  r@  Úspend_logs_is_partitionedrñ   rD  s     r   Ú _raise_if_partitioned_spend_logsz.PrismaManager._raise_if_partitioned_spend_logsz  s?   € ð	÷ð  ×9Ñ9Ô;ÜÐ@ÓAÐAð <øô ò 	Ùð	ús   ‚& ¦	2±2Úuse_migrateÚuse_v2_resolverc                 óV  — t        d«      D ]à  }t        j                  «       }t        j	                  «       }t        j
                  |«       	 | rE	 ddlm} t        j	                  «       }|j                  | |¬«      t        j
                  |«       c S t        j                  «        t        j                  g d¢d	d
¬«       t        j                  «        	 t        j
                  |«        y
 y# t        $ r6}t        j                  d|«       Y d}~t        j
                  |«        yd}~ww xY w# t        j                   $ rE t        j"                  d|dz   «       t%        j&                  t)        j*                  dd«      «       Y nqt        j,                  $ r\}d|z
  }|dkD  rd|› d�nd}t        j"                  d||«       t%        j&                  t)        j*                  dd«      «       Y d}~nd}~ww xY wt        j
                  |«       �Œ# t        j
                  |«       w xY w)aÛ  
        Set up the database using either prisma migrate or prisma db push

        Args:
            use_migrate: Use `prisma migrate deploy` instead of `db push`.
            use_v2_resolver: Opt into the v2 migration resolver that avoids
                the diff-and-force recovery behavior (which caused schema
                thrashing during rolling deploys). Defaults to False.

        Returns:
            bool: True if setup was successful, False otherwise
        é   r   r=  z9[1;31mLiteLLM: Failed to import proxy extras. Got %s[0mNF)rH  rI  )r	  ÚdbÚpushz--accept-data-lossz--skip-generater  T)r¢   ÚcheckzAttempt %s timed outrI   é   é   é   z Retrying... (z attempts left)rp   z-The process failed to execute. Details: %s.%s)Úranger´   Úgetcwdr   r;  Úchdirr?  r>  r@  r	   r   Úsetup_databaserG  Ú
subprocessr+  rB  ÚTimeoutExpiredr¦   Útimer»   ÚrandomÚ	randrangeÚCalledProcessError)	rH  rI  ÚattemptÚoriginal_dirÚ
prisma_dirr>  r"  Úattempts_leftÚ	retry_msgs	            r   rU  zPrismaManager.setup_databaseŠ  sÉ  € ô ˜Q“xò +	'ˆGÜŸ9™9›;ˆLÜ&×6Ñ6Ó8ˆJÜ�H‰H�ZÔ ð''Ùð%ÝSô
 "/×!>Ñ!>Ó!@�Jà/×>Ñ>Ø$/Ø(7ð ?ó ô: —‘˜×&ô1 "×BÑBÔDä—N‘Nòð !#Ø"õ
ô "×KÑKÔMØô —‘˜Ö&ðW+	'ðX øôI 'ò %Ü,×2Ñ2Ð3tÐvwÔxÛ$ôB —‘˜Ö&ûðG%ûô4 ×,Ñ,ò 4Ü$×,Ñ,Ð-CÀWÈqÁ[ÔQÜ—
‘
œ6×+Ñ+¨A¨rÓ2Ö3Ü×0Ñ0ò 4Ø ! G¡�ØO\Ð_`ÒO`˜n¨]¨O¸?ÑKÐfh�	Ü$×,Ñ,Ð-\Ð^_ÐajÔkÜ—
‘
œ6×+Ñ+¨A¨rÓ2×3Ñ3ûð	4úô —‘˜Ö&ø”—‘˜Õ&úsi   ÁD2ÁC0Á&D2ÂAD2Ã0	D/Ã9D*ÄD2Ä*D/Ä/D2Ä2AG7ÆHÆ	G7ÆAG2Ç-HÇ2G7Ç7HÈH(rY   )FF)
r   r   r   r1  r6   r;  rB  rG  r0  rU  r   r   r   r   r   c  sq   „ Øð˜Sò ó ðð òHó ðHð òBó ðBð ñ: Dð :À4ð :ÐTXò :ó ñ:r   r   Údisable_updatesr!   c                 ó€   — | €t        j                  dd«      } t        | t        «      rt	        | «      } t        | «       S )ao  
    Determines if Prisma Schema updates should be applied during startup.

    Args:
        disable_updates: Controls whether schema updates are disabled.
            Accepts boolean or string ('true'/'false'). Defaults to checking DISABLE_SCHEMA_UPDATE env var.

    Returns:
        bool: True if schema updates should be applied, False if updates are disabled.

    Examples:
        >>> should_update_prisma_schema()  # Checks DISABLE_SCHEMA_UPDATE env var
        >>> should_update_prisma_schema(True)  # Explicitly disable updates
        >>> should_update_prisma_schema("false")  # Enable updates using string
    ÚDISABLE_SCHEMA_UPDATEÚfalse)r´   rÊ   rš   r6   r   r0  )ra  s    r   Úshould_update_prisma_schemare  È  s=   € ð$ ÐÜŸ)™)Ð$;¸WÓEˆä�/¤3Ô'Ü% oÓ6ˆä�OÓ$Ð$Ð$r   r#   )(r/  rC   r´   rY  r¶   rV  rX  r¾   Úurllib.parseÚcollections.abcr   r   r   Útypingr   r   r   Úlitellm._loggingr	   Úlitellm.proxy.db.token_authr
   r   r   r   r   r   r   Úlitellm.secret_managers.mainr   Ú__all__r   r    r8   r=   r[   r   r   r0  r6   re  r   r   r   ú<module>rm     sÆ   ðñó
 Û 	Û Û Û Û Û Û Ý $ß (ß 'Ñ 'å 1÷÷ ñ õ 5ð€ô�Xô ô
B�Hô 
Bô,�Hô ,÷#ñ #÷<+5ñ +5÷\Vñ V÷rbñ bðL *.ñ%Ø˜C‘Z $Ñ&ð%à	ô%r   