o
    Lj]                     @   s  d dl Z d dlZd dlZd dlmZmZmZmZ d dlm	Z	m
Z
mZ d dlmZmZmZmZmZmZmZmZmZ d dlmZ erLd dlmZ d dlmZ eeZeded f Zd	e d
dddfddZ!de"de eB ddfddZ#G dd dZ$G dd dZ%G dd dZ&dS )    N)TYPE_CHECKINGAny	AwaitableCallable)record_connection_handoff!record_connection_relaxed_timeoutrecord_maint_notification_count)	MaintenanceNotificationMaintenanceStateMaintNotificationsConfigNodeMovingNotificationOSSNodeMigratedNotificationOSSNodeMigratingNotification"_get_maintenance_notification_name"_get_maintenance_notification_type&_should_skip_connection_timeout_update)get_pool_name)RedisCluster))AsyncMaintNotificationsAbstractConnection.messagetaskzasyncio.Task[Any]returnc                 C   s>   z|  }W n tjy   Y dS w |rtj| |d dS dS )a  Task done-callback that surfaces a failed task's exception in the logs.

    Without it, an unhandled exception in a fire-and-forget task is only
    reported by asyncio as a noisy "Task exception was never retrieved"
    warning. Retrieving the exception here suppresses that warning and logs a
    meaningful error instead. Cancellation is expected and left unlogged.

    Bind ``message`` with ``functools.partial`` before passing to
    ``add_done_callback`` (which supplies ``task``).
    N)exc_info)	exceptionasyncioCancelledErrorloggererror)r   r   exc r   _/home/djax/ivt_ai_plugin/venv/lib/python3.10/site-packages/redis/asyncio/maint_notifications.py_log_task_exception!   s   r!   
connectionnotificationc              
   C   s   t tjsd S d }zt| dd }|r|dnd }|r&t|dkr&|d nd }W n ttt	fy5   Y nw d }zt| dd }t
|rF| }W n ttfyR   Y nw t d| d|  d| d|  d S )	N_writersockname   get_resolved_ipz#Handling maintenance notification: , with connection: , connected to ip z, local socket port: )r   isEnabledForloggingDEBUGgetattrget_extra_infolenAttributeErrorOSError	TypeErrorcallabledebug)r"   r#   socket_addresswritersocket_nameresolved_ipr'   r   r   r    add_debug_log_for_notification4   s:   r9   c                   @   s   e Zd ZdededdfddZ				d#d	d
Zd$ddZd%ddZde	ddfddZ
deddfddZ	d&dedB ddfddZdeddfddZdedededdfddZdedededdfdd Zd%d!d"ZdS )'"AsyncMaintNotificationsPoolHandlerpoolconfigr   Nc                 C   s0   || _ || _t | _t | _t | _d | _d S N)	r;   r<   set_processed_notifications_scheduled_tasksr   Lock_lockr"   )selfr;   r<   r   r   r    __init__U   s   

z+AsyncMaintNotificationsPoolHandler.__init__r"   r   c                 C   s
   || _ d S r=   )r"   )rC   r"   r   r   r    set_connectiona   s   
z1AsyncMaintNotificationsPoolHandler.set_connectionc                 C   s0   t | j| j}| j|_| j|_| j|_d |_|S r=   )r:   r;   r<   r?   r@   rB   r"   )rC   copyr   r   r    get_handler_for_connectionf   s   z=AsyncMaintNotificationsPoolHandler.get_handler_for_connectionc              	      f   | j 4 I d H  t| jD ]}| r| j| qW d   I d H  d S 1 I d H s,w   Y  d S r=   rB   tupler?   
is_expiredremoverC   r#   r   r   r    remove_expired_notificationsp      .z?AsyncMaintNotificationsPoolHandler.remove_expired_notificationsr#   c                    sB   |   I d H  t|tr| |I d H  d S td|  d S NUnhandled notification type: )rN   
isinstancer   handle_node_moving_notificationr   r   rM   r   r   r    handle_notificationv   s
   
z6AsyncMaintNotificationsPoolHandler.handle_notificationc              
      sN  | j js| j  sd S | j4 I d H  || jv r%	 W d   I d H  d S ttjrBt	d| d| j
 d| j
r=| j
 nd   | j
rJ| j
 nd }| jj|| j || j jo[|jd udI d H  | j jru|jd u ru| |jd | j| | |j| j| tt| jdI d H  | j| W d   I d H  d S 1 I d H sw   Y  d S )Nz#Handling node MOVING notification: r(   r)   )r#   r<   moving_address_srcrun_proactive_reconnect   	pool_name)r<   proactive_reconnectis_relaxed_timeouts_enabledrB   r?   r   r*   r+   r,   r4   r"   r'   getpeernamer;   apply_moving_notificationnew_node_host	_schedulettlrV   handle_node_moved_notificationr   r   add)rC   r#   rU   r   r   r    rS   ~   sV   


.zBAsyncMaintNotificationsPoolHandler.handle_node_moving_notificationrU   c              	      sV   | j 4 I dH  | jj|dI dH  W d  I dH  dS 1 I dH s$w   Y  dS )z
        Run proactive reconnect for the pool.
        Active connections are marked for reconnect after they complete the current command.
        Inactive connections are disconnected and will be connected on next use.
        N)rU   )rB   r;   rV   )rC   rU   r   r   r    rV      s   .z:AsyncMaintNotificationsPoolHandler.run_proactive_reconnectc              
      s   t |}| j4 I dH > ttjr*td| d| j d| jr%| j nd  | j	
 }| j	j}| jj|||dI dH  W d  I dH  dS 1 I dH sPw   Y  dS )zN
        Handle the cleanup after a node moving notification expires.
        Nz5Reverting temporary changes related to notification: r(   r)   )notification_hashreset_relaxed_timeoutreset_host_address)hashrB   r   r*   r+   r,   r4   r"   r'   r<   r[   rZ   r;   cleanup_moving_notification)rC   r#   rc   rd   re   r   r   r    ra      s&   
.zAAsyncMaintNotificationsPoolHandler.handle_node_moved_notificationdelaycallbackargsc                 G   sZ   t   | }t | j||g|R  }| j| || jj |t	
td d S )Nz1Error handling scheduled maintenance notification)r   get_running_looptimecreate_task
_run_afterr@   rb   add_done_callbackdiscard	functoolspartialr!   )rC   rh   ri   rj   deadliner   r   r   r    r_      s   z,AsyncMaintNotificationsPoolHandler._schedulers   c                    s<   |t    }|dkrt |I d H  || I d H  d S )Nr   )r   rk   rl   sleep)rC   rs   ri   rj   	remainingr   r   r    rn      s
   z-AsyncMaintNotificationsPoolHandler._run_afterc                    sB   | j sd S t| j }|D ]}|  qtj|ddiI d H  d S )Nreturn_exceptionsT)r@   rJ   cancelr   gather)rC   tasksr   r   r   r    cancel_scheduled_tasks  s   

z9AsyncMaintNotificationsPoolHandler.cancel_scheduled_tasks)r"   r   r   N)r   r:   r   Nr=   )__name__
__module____qualname__r   r   rD   rE   rG   rN   r	   rT   r   rS   strrV   ra   float_ScheduledCallbackr_   rn   rz   r   r   r   r    r:   T   sf    





?



r:   c                   @   sj   e Zd ZdddeddfddZdefdd	Zd
eddfddZde	d
eddfddZ
deddfddZdS )(AsyncMaintNotificationsConnectionHandlerr"   r   r<   r   Nc                 C   s   || _ || _d S r=   )r"   r<   )rC   r"   r<   r   r   r    rD     s   
z1AsyncMaintNotificationsConnectionHandler.__init__c                 C   s2   t | jdd}|rt |ddrt|jS t| jS )z
        Get the pool name from the connection's pool handler.
        Falls back to connection representation if pool is not available.
        !_maint_notifications_pool_handlerNr;   )r-   r"   r   r;   repr)rC   pool_handlerr   r   r    _get_pool_name  s   

z7AsyncMaintNotificationsConnectionHandler._get_pool_namer#   c                    s   t |}t|}t| jj| jj| jj| jj|dI d H  |d u r+td|  d S |r9| t	j
|I d H  d S | j|dI d H  d S )N)server_addressserver_portnetwork_peer_addressnetwork_peer_portmaint_notificationrQ   )r#   )r   r   r   r"   hostportr   r   %handle_maintenance_start_notificationr
   MAINTENANCE)handle_maintenance_completed_notification)rC   r#   notification_typer   r   r   r    rT   $  s(   z<AsyncMaintNotificationsConnectionHandler.handle_notificationmaintenance_statec                    s   t | j| t| jj| jrd S || j_| jj| jjd | j| jj t|t	r2| j
|j t|}t|  |ddI d H  d S )N)tmp_relaxed_timeoutTconnection_namer   relaxed)r9   r"   r   r   r<   set_tmp_settingsrelaxed_timeoutupdate_current_socket_timeoutrR   r   add_maint_start_notificationidr   r   r   )rC   r   r#   r   r   r   r    r   >  s&   

zNAsyncMaintNotificationsConnectionHandler.handle_maintenance_start_notificationkwargsc                    s   t | jj| jrd S d }|dr|d }t| j|r|nd | jjdd | jd tj	| j_| j
  |rKt|}t|  |ddI d H  d S d S )Nr#   MAINTENANCE_COMPLETEDT)rd   Fr   )r   r"   r   r<   getr9   reset_tmp_settingsr   r
   NONEreset_received_notificationsr   r   r   )rC   r   r#   r   r   r   r    r   \  s.   



zRAsyncMaintNotificationsConnectionHandler.handle_maintenance_completed_notification)r|   r}   r~   r   rD   r   r   r	   rT   r
   r   r   r   r   r   r   r    r     s$    

r   c                   @   sf   e Zd ZdZdddeddfddZdd	d
ZdeddfddZdeddfddZ	de
ddfddZdS )!AsyncOSSMaintNotificationsHandleru0  
    Cluster-wide handler for OSS (open-source) cluster maintenance notifications.

    Reacts to SMIGRATED (slot migration completed) push notifications and
    triggers topology re-initialization via nodes_manager.initialize().

    Lock discipline: _lock is held across the whole pool mutation, including
    await initialize(), so the topology refresh and the subsequent connection
    marking/disconnect happen as one atomic operation — releasing the lock
    mid-mutation would let other tasks observe partial state.

    Re-entrancy is not a deadlock risk here even though asyncio.Lock is
    non-reentrant: initialize() may dispatch commands whose responses carry new
    push notifications, but handle_notification schedules the actual handling as
    a separate background task rather than calling into it inline, so the
    re-entrant arrival never tries to re-acquire the lock on this call stack.
    The cheap _in_progress/_processed dedup that gates that scheduling runs
    without the lock — those sets are only mutated from the single event loop.
    cluster_clientr   r<   r   Nc                 C   s2   || _ || _t | _t | _t | _t | _d S r=   )	r   r<   r>   r?   _in_progressr   rA   rB   _background_tasks)rC   r   r<   r   r   r    rD     s   
z*AsyncOSSMaintNotificationsHandler.__init__c              	      rH   r=   rI   )rC   nr   r   r    rN     rO   z>AsyncOSSMaintNotificationsHandler.remove_expired_notificationsr#   c                    s   || j v s|| jv rd S | j | z#t | |}| j| || jj	 |t
td W d S  tyD   | j 	|  w )Nz7Error handling maintenance notification background task)r   r?   rb   r   rk   rm   _do_handle_notificationr   ro   rp   rq   rr   r!   	Exception)rC   r#   r   r   r   r    rT     s*   


z5AsyncOSSMaintNotificationsHandler.handle_notificationc              	      sb   z(t |tr| |I d H  ntd|  W | j| d S W | j| d S | j| w rP   )rR   r   -handle_oss_maintenance_completed_notificationr   r   r   rp   rM   r   r   r    r     s   
z9AsyncOSSMaintNotificationsHandler._do_handle_notificationc              	      s`  |   I d H  | j4 I d H  || jv r"	 W d   I d H  d S ttjr0td|  g }t }|j	
 D ]=\}}|dd\}}| jjj|t|d}|d urZ|| |D ]}	|	 D ]}
|
dd\}}||t|f qbq\q:| jjj|dI d H  t|}|| jjj }|D ]}}d}||v rt|j}|jD ]}||vrt|d |  qt|j d|j dI d H  d	}nttjrtd
|j d || jjj vrt  !|" }| j#| |$| j#j% |$t&'t(d |st|j d|j dI d H  q| j| W d   I d H  d S 1 I d H s)w   Y  d S )Nz!Handling SMIGRATED notification: :r&   )r   r   )additional_startup_nodes_infoFzSMIGRATED - mark for reconnectrX   TzSMIGRATED: Node z9 not affected by maintenance, skipping mark for reconnectzCError disconnecting free connections after maintenance notification))rN   rB   r?   r   r*   r+   r,   r4   r>   nodes_to_slots_mappingitemsrsplitr   nodes_managerget_nodeintrb   keysappend
initializeunionnodes_cachevalues_free_connectionsr9   mark_for_reconnectr   r   r   namer   rk   rm   disconnect_free_connectionsr   ro   rp   rq   rr   r!   )rC   r#   r   affected_nodessrc_addressdest_mappingssrc_hostsrc_portsrc_nodedest_mappingdest_address	dest_host	dest_port	all_nodescurrent_nodehandoff_recordedfree_setconnr   r   r   r    r     s   




0zOAsyncOSSMaintNotificationsHandler.handle_oss_maintenance_completed_notificationr{   )r|   r}   r~   __doc__r   rD   rN   r	   rT   r   r   r   r   r   r   r    r   {  s*    

.
r   )'r   rq   r+   typingr   r   r   r   $redis.asyncio.observability.recorderr   r   r   redis.maint_notificationsr	   r
   r   r   r   r   r   r   r   redis.observability.attributesr   redis.asyncio.clusterr   redis.asyncio.connectionr   	getLoggerr|   r   r   r   r!   objectr9   r:   r   r   r   r   r   r    <module>   s0    ,

  ;m