
    $ja$                     V   U d 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  ej        e          Ze G d d                      Z G d d	          Z G d
 d          Zdaedz  ed<   ddedefdZe	 	 	 ddedz  dedz  dedee         fd            Z G d d          ZdS )z
Database connection budget management.

Limits concurrent database connections per operation to prevent
a single operation (e.g., recall with parallel queries) from
exhausting the connection pool.
    N)asynccontextmanager)	dataclassfield)AnyAsyncIteratorc                   ~    e Zd ZU dZeed<   eed<    ed          Ze	j
        ed<    edd          Zeed	<   d
 ZdS )OperationBudgetz
    Tracks connection budget for a single operation.

    Each operation gets a semaphore limiting its concurrent connections.
    operation_idmax_connectionsF)init	semaphorer   )defaultr   active_countc                 B    t          j        | j                  | _        d S N)asyncio	Semaphorer   r   selfs    f/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/hindsight_api/engine/db_budget.py__post_init__zOperationBudget.__post_init__    s     *4+?@@    N)__name__
__module____qualname____doc__str__annotations__intr   r   r   r   r   r    r   r   r	   r	      s           #(5e#4#4#4Iw 444ae444L#444A A A A Ar   r	   c            	       r    e Zd ZdZddefdZe	 	 ddedz  dedz  ded	         fd
            Z	dede
fdZdS )ConnectionBudgetManageraA  
    Manages per-operation connection budgets.

    Usage:
        manager = ConnectionBudgetManager(default_budget=4)

        # Start an operation
        async with manager.operation(max_connections=2) as op:
            # Acquire connections within the budget
            async with op.acquire(pool) as conn:
                await conn.fetch(...)

            # Multiple connections respect the budget
            async with op.acquire(pool) as conn1, op.acquire(pool) as conn2:
                # At most 2 concurrent connections for this operation
                ...
       default_budgetc                 R    || _         i | _        t          j                    | _        dS )z
        Initialize the budget manager.

        Args:
            default_budget: Default max connections per operation
        N)r$   _operationsr   Lock_lock)r   r$   s     r   __init__z ConnectionBudgetManager.__init__7   s%     -79\^^


r   Nr   r
   returnBudgetedOperationc                  K   |p"dt          j                    j        dd          }|p| j        }| j        4 d{V  || j        v rt          d| d          t          ||          | j        |<   ddd          d{V  n# 1 d{V swxY w Y   	 t          | |          W V  | j        4 d{V  | j        	                    |d           ddd          d{V  dS # 1 d{V swxY w Y   dS # | j        4 d{V  | j        	                    |d           ddd          d{V  w # 1 d{V swxY w Y   w xY w)a{  
        Create a budgeted operation context.

        Args:
            max_connections: Max concurrent connections for this operation.
                           Defaults to manager's default_budget.
            operation_id: Optional custom operation ID. Auto-generated if not provided.

        Yields:
            BudgetedOperation context for acquiring connections
        zop-N   
Operation z already exists)
uuiduuid4hexr$   r(   r&   
ValueErrorr	   r+   pop)r   r   r
   op_idbudgets        r   	operationz!ConnectionBudgetManager.operationB   s(     " = =djll&6ss&; = = 7D$7: 	E 	E 	E 	E 	E 	E 	E 	E((( !De!D!D!DEEE&5eV&D&DDU#	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E 	E
	2#D%000000z 2 2 2 2 2 2 2 2 $$UD1112 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2tz 2 2 2 2 2 2 2 2 $$UD1112 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2sS   5B
BBC9 7C&&
C03C09ED5#E5
D??ED?Ec                 d    | j                             |          }|st          d| d          |S )z+Get budget for an operation (internal use).r.   z
 not found)r&   getr2   )r   r
   r5   s      r   _get_budgetz#ConnectionBudgetManager._get_budgeta   s@    !%%l33 	DB,BBBCCCr   r#   )NN)r   r   r   r   r   r)   r   r   r   r6   r	   r9   r    r   r   r"   r"   $   s         $	$ 	$s 	$ 	$ 	$ 	$  '+#'2 2t2 Dj2 
*	+	2 2 2 2<       r   r"   c                       e Zd ZdZdedefdZedefd            Z	e
dedee         fd            Zdedd	fd
Zdededeee                  fdZdS )r+   zt
    A single operation with connection budget.

    Provides methods to acquire connections within the budget.
    managerr
   c                 "    || _         || _        d S r   )_managerr
   )r   r<   r
   s      r   r)   zBudgetedOperation.__init__p   s    (r   r*   c                 @    | j                             | j                  S )z"Get the budget for this operation.)r>   r9   r
   r   s    r   r5   zBudgetedOperation.budgett   s     }(():;;;r   poolc                X  K   | j         }|j        4 d{V  |xj        dz  c_        	 ddlm} t          ||          rD|                                4 d{V }|W V  ddd          d{V  n# 1 d{V swxY w Y   n\|                                 d{V }	 |W V  |                    |           d{V  n # |                    |           d{V  w xY w|xj        dz  c_        n# |xj        dz  c_        w xY w	 ddd          d{V  dS # 1 d{V swxY w Y   dS )a  
        Acquire a connection within the operation's budget.

        Blocks if the operation has reached its connection limit.

        Args:
            pool: asyncpg connection pool or DatabaseBackend

        Yields:
            Database connection
        N   DatabaseBackend)r5   r   r   db.baserD   
isinstanceacquirerelease)r   r@   r5   rD   conns        r   rG   zBudgetedOperation.acquirey   s      # 	) 	) 	) 	) 	) 	) 	) 	)1$)444444dO44 1#||~~ # # # # # # #"



# # # # # # # # # # # # # # # # # # # # # # # # # # # "&//////D1"



"ll40000000000dll40000000000##q(#####q(#######	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	)sd   D0C0A1C01
A;	;C0>A;	?C0B?#C0?CC0D0DD
D#&D#BudgetedPoolc                 "    t          ||           S )av  
        Wrap a pool with this operation's budget.

        The returned BudgetedPool can be passed to functions expecting a pool,
        and all acquire() calls will be limited by this operation's budget.

        Args:
            pool: asyncpg connection pool to wrap

        Returns:
            BudgetedPool that limits connections to this operation's budget
        )rJ   )r   r@   s     r   	wrap_poolzBudgetedOperation.wrap_pool   s     D$'''r   countc                &  K   g }	 t          |          D ]1}|                                 d{V }|                    |           2|W V  |D ]}|                    |           d{V  dS # |D ]}|                    |           d{V  w xY w)a  
        Acquire multiple connections within the budget.

        Note: This acquires connections sequentially to respect the budget.
        For parallel acquisition, use multiple acquire() calls with asyncio.gather().
        This method is intended for use with raw asyncpg pools only, not DatabaseBackend.

        Args:
            pool: asyncpg connection pool (raw pool only)
            count: Number of connections to acquire

        Yields:
            List of database connections
        N)rangerG   appendrH   )r   r@   rM   connections_rI   s         r   acquire_manyzBudgetedOperation.acquire_many   s      & 	)5\\ ) )!\\^^++++++""4((((# ) )ll4(((((((((() ) ) )ll4(((((((((()s   AA. ."BN)r   r   r   r   r"   r   r)   propertyr	   r5   r   r   r   rG   rL   r   listrS   r    r   r   r+   r+   i   s         ) 7 )s ) ) ) ) < < < < X< )# )-*< ) ) ) )<(c (n ( ( ( ()) ) 
tCy	!	) ) ) ) ) )r   r+   _default_managerr#   r$   r*   c                 >    t           t          |           a t           S )z
    Get or create the global budget manager.

    Args:
        default_budget: Default max connections per operation

    Returns:
        Global ConnectionBudgetManager instance
    Nr$   )rV   r"   rX   s    r   get_budget_managerrY      s!     2.QQQr   r   r
   c                   K   t          |          }|                    | |          4 d{V }|W V  ddd          d{V  dS # 1 d{V swxY w Y   dS )a  
    Convenience function to create a budgeted operation.

    Args:
        max_connections: Max concurrent connections for this operation
        operation_id: Optional custom operation ID
        default_budget: Default budget if manager not yet created

    Yields:
        BudgetedOperation context

    Example:
        async with budgeted_operation(max_connections=2) as op:
            async with op.acquire(pool) as conn:
                await conn.fetch(...)
    N)rY   r6   )r   r
   r$   r<   ops        r   budgeted_operationr\      s      , !00G  ,??       2                             s   A
AAc                   d    e Zd ZdZdZdedefdZede	e         fd            Z
dedd	fd
Zd Zd	S )rJ   a  
    A pool wrapper that limits concurrent connection acquisitions.

    This can be passed to functions expecting a pool, and acquire()
    calls will be limited by the budget semaphore.

    Usage:
        async with budgeted_operation(max_connections=4) as op:
            budgeted_pool = op.wrap_pool(pool)
            # Pass budgeted_pool to functions that expect a pool
            await some_function(budgeted_pool, ...)
    Tr@   r6   c                 "    || _         || _        d S r   )_pool
_operation)r   r@   r6   s      r   r)   zBudgetedPool.__init__  s    
#r   r*   c                  K   | j         j        }|j                                         d{V  |xj        dz  c_        	 ddlm} t          | j        |          rI| j                                        4 d{V }|W V  ddd          d{V  n# 1 d{V swxY w Y   nk| j                                         d{V }	 |W V  | j        	                    |           d{V  n%# | j        	                    |           d{V  w xY wn# t          $ r  w xY w|xj        dz  c_        |j        	                                 dS # |xj        dz  c_        |j        	                                 w xY w)z
        Acquire a connection within the budget as an async context manager.

        The connection is automatically released when the context exits.
        NrB   rC   )r`   r5   r   rG   r   rE   rD   rF   r_   rH   	Exception)r   r5   rD   rI   s       r   rG   zBudgetedPool.acquire  sV      '&&(((((((((q 	'000000$*o66 3:--//       4JJJJ                           "Z//111111113JJJJ*,,T2222222222$*,,T2222222222 	 	 		 1$$$&&&&& 1$$$&&&&sY   :D 9B?D 
BD B#D C) !D )"DD E	 DE	 	+E4rI   Nc                   K   | j         j        }	 | j                            |           d{V  |xj        dz  c_        |j                                         dS # |xj        dz  c_        |j                                         w xY w)z9Release a connection back to the pool (legacy path only).NrB   )r`   r5   r_   rH   r   r   )r   rI   r5   s      r   rH   zBudgetedPool.release$  s      '	'*$$T*********1$$$&&&&& 1$$$&&&&s    A +Bc                 ,    t          | j        |          S )z.Proxy other attributes to the underlying pool.)getattrr_   )r   names     r   __getattr__zBudgetedPool.__getattr__-  s    tz4(((r   )r   r   r   r   _wraps_backendr   r+   r)   r   r   rG   rH   rg   r    r   r   rJ   rJ      s          N$S $-> $ $ $ $ '}S1 ' ' ' '6'# '$ ' ' ' ') ) ) ) )r   rJ   r:   )NNr#   )r   r   loggingr/   
contextlibr   dataclassesr   r   typingr   r   	getLoggerr   loggerr	   r"   r+   rV   r   r   rY   r   r\   rJ   r    r   r   <module>ro      s        * * * * * * ( ( ( ( ( ( ( ( % % % % % % % %		8	$	$ A A A A A A A A B B B B B B B BJY) Y) Y) Y) Y) Y) Y) Y)z 48 )D0 7 7 7 s 3J      "&# 4Z*  $%	   4;) ;) ;) ;) ;) ;) ;) ;) ;) ;)r   