
    KinE                     .   U d dl Z d dlZd dlZd dlmZmZ d dlmZ d dlm	Z	m
Z
mZmZmZ d dlmZ d dlmZ d dlmZmZ d dlmZ d dlmZ d dlmZ d d	lmZ d d
lm Z  d dl!m"Z" eeef   Z#dede$fdZ%de&dede&fdZ'dZ(dZ)dZ*dZ+dZ, ejZ                  e.      Z/ G d de      Z0 G d de      Z1 G d de1      Z2 G d de2      Z3 G d de2      Z4 G d d e2      Z5 G d! d"e      Z6e6jn                  Z8e6e9d#<    G d$ d%e0      Z: G d& d'e:      Z; G d( d)e:      Z<y)*    N)ABCabstractmethod)Enum)ListOptionalTupleTypeUnion)Redis)RedisCluster)DEFAULT_TIMEOUTAsyncHTTPClientWrapper)	NoBackoff)
HttpClient)UnhealthyDatabaseException)Retryclsreturnc                     t        j                  | j                        }t        d |j                  j                         D              S )z7Extract parameter names from a class's __init__ method.c              3      K   | ]N  \  }}|d k7  rD|j                   t        j                  j                  t        j                  j                  fv r| P yw)selfN)kindinspect	ParameterPOSITIONAL_OR_KEYWORDKEYWORD_ONLY).0nameparams      m/home/jay/workspace/scripts/.codegraph-venv/lib/python3.12/site-packages/redis/asyncio/multidb/healthcheck.py	<genexpr>z#_get_init_params.<locals>.<genexpr>   sR      	D%6>JJ33**

 		s   AA)r   	signature__init__	frozenset
parametersitems)r   sigs     r    _get_init_paramsr(      s=    


CLL
)C 	>>//1	 	 	    kwargsc                 v    t        |      }| j                         D ci c]  \  }}||v s|| c}}S c c}}w )zJFilter kwargs to only include parameters accepted by the class's __init__.)r(   r&   )r*   r   allowedkvs        r    _filter_kwargsr/   %   s4    s#G#\\^<TQqG|AqD<<<s   55      g      ?i  c                       e Zd ZdZeedefd              Zeedefd              Z	eedefd              Z
ededefd       Zy)	HealthCheckz!
    Health check interface.
    r   c                      y)z*Number of probes to execute health checks.N r   s    r    health_check_probeszHealthCheck.health_check_probes9        	r)   c                      y)z"Delay between health check probes.Nr5   r6   s    r    health_check_delayzHealthCheck.health_check_delay?   r8   r)   c                      y)zCTimeout for the full health check operation (including all probes).Nr5   r6   s    r    health_check_timeoutz HealthCheck.health_check_timeoutE   r8   r)   	hc_clientc                    K   yw)ap  
        Function to determine the health status.

        Args:
            database: The database being checked
            hc_client: A Redis client (AsyncRedis or AsyncRedisCluster) to use for
                health checks. This client follows topology changes automatically.

        Returns:
            True if the database is healthy, False otherwise.
        Nr5   r   databaser=   s      r    check_healthzHealthCheck.check_healthK   s      	   N)__name__
__module____qualname____doc__propertyr   intr7   floatr:   r<   AsyncRedisClientTboolrA   r5   r)   r    r3   r3   4   s     S    E    e    6G D  r)   r3   c                   r    e Zd ZdZedee   defd       Zededefd       Z	ede
fd       Zed
d	       Zy)HealthCheckPolicyz)
    Health checks execution policy.
    health_checksr   c                    K   yw)z8Execute health checks and return database health status.Nr5   )r   rN   r@   s      r    executezHealthCheckPolicy.execute`         	rB   health_checkc                    K   ywz?
        Executes health check against given database.
        Nr5   r   rR   r@   s      r    _executezHealthCheckPolicy._executee        
 	rB   c                    K   yw)z=
        Get a health check client for the database.
        Nr5   )r   r@   s     r    
get_clientzHealthCheckPolicy.get_clientl   rW   rB   Nc                    K   yw)Close all health check clients.Nr5   r6   s    r    closezHealthCheckPolicy.closes   rQ   rB   r   N)rC   rD   rE   rF   r   r   r3   rK   rP   rV   rJ   rY   r\   r5   r)   r    rM   rM   [   s     4+< 4   ; T   ,=    r)   rM   c                   Z    e Zd ZdZd Zdee   defdZde	fdZ
ddZed	edefd
       Zy)AbstractHealthCheckPolicyz'
    Abstract health check policy.
    c                     i | _         y N)_clientsr6   s    r    r#   z"AbstractHealthCheckPolicy.__init__~   s	    68r)   rN   r   c                     K   dt         f fd}t        j                  |D cg c]
  } ||       c}ddi d{   }|D ]#  }t        |t              rt        d|      |r# y yc c}w 7 2w)a  
        Execute all health checks concurrently with individual timeouts.
        Each health check runs with its own timeout, and all run in parallel.

        All exception handling is centralized here - _execute() methods just
        propagate exceptions naturally.
        rR   c                    K   t        j                  j                  |       | j                         d {   S 7 w)N)timeout)asynciowait_forrV   r<   )rR   r@   r   s    r    execute_with_timeoutz?AbstractHealthCheckPolicy.execute.<locals>.execute_with_timeout   s;      ))lH5$99   s   5?=?return_exceptionsTNzUnhealthy databaseF)r3   rf   gather
isinstance	Exceptionr   )r   rN   r@   rh   hcresultsresults   ` `    r    rP   z!AbstractHealthCheckPolicy.execute   s     	[ 	  1>?2"2&?
"
 
  	F&),01ExQWXX	  @
s   !A3A,
A3A1(A3(
A3c           
        K   t        |      }| j                  j                  |      }|t        |j                  t
        t        f      r7|j                  j                         }t        |t
              }t        di |}n6t        |j                  t        t        f      r|j                  j                         j                         }t        |t              }|j                  j                  }|r|d   }t        d|j                  |j                  |j                  j                  j                   |j                  j                  j"                  |j                  j                  j$                  |j                  j&                  d|}n,t)        d      t+        dt-        |j                               || j                  |<   |S w)a  
        Get or create a health check client for the database.

        Creates a single client instance per database that follows topology
        changes automatically. For cluster databases, the client handles
        node discovery and slot mapping internally.
        r   )hostportdynamic_startup_nodesaddress_remaprequire_full_coverageretryz?Cluster client has no nodes - cannot create health check clientzUnsupported client type: r5   )idrb   getrk   client
AsyncRedis	SyncRedisget_connection_kwargsr/   AsyncRedisClusterSyncRedisClustercopystartup_nodesrq   rr   nodes_manager_dynamic_startup_nodesrt   _require_full_coveragerv   
ValueError	TypeErrortype)r   r@   db_idry   conn_kwargsfiltered_kwargsr   
first_nodes           r    rY   z$AbstractHealthCheckPolicy.get_client   st     8""5)>(//J	+BC&ooCCE"0j"I#6o6HOO.?AQ-RS 'ooCCEJJL"0>O"P ( = = !.q!1J. '__'__.6oo.K.K.b.b&.oo&C&C&Q&Q.6oo.K.K.b.b&oo33 *F %Y   ";D<Q;R STT#)DMM% s   GG
Nc                   K   | j                   j                         D cg c]%  }t        j                  |j	                               ' }}|rt        j
                  |ddi d{    | j                   j                          yc c}w 7 $w)r[   ri   TN)rb   valuesrf   create_taskacloserj   clear)r   ry   close_taskss      r    r\   zAbstractHealthCheckPolicy.close   sy      @D}}?S?S?U
5;G0
 
 ..+FFFF

 Gs   B*B	B%B
&%BrR   c                    K   ywrT   r5   rU   s      r    rV   z"AbstractHealthCheckPolicy._execute   rW   rB   r]   )rC   rD   rE   rF   r#   r   r3   rK   rP   rJ   rY   r\   r   rV   r5   r)   r    r_   r_   y   sZ    94+< 4 B+,= +Z	 ; T  r)   r_   c                        e Zd ZdZdedefdZy)HealthyAllPolicyzM
    Policy that returns True if all health check probes are successful.
    rR   r   c                   K   | j                  |       d{   }|j                  }t        |      D ]P  }|j                  ||       d{   }|s y||dz
  k  s*t	        j
                  |j                         d{    R y7 o7 =7 w)
        Executes health check against given database.

        Uses a single client that handles topology changes automatically.
        NF   T)rY   r7   rangerA   rf   sleepr:   )r   rR   r@   ry   probesattemptro   s          r    rV   zHealthyAllPolicy._execute   s      x0011V} 	EG'44XvFFF!#mmL$C$CDDD	E  1 G
 Es9   BB3BB	B"B?B B	BBNrC   rD   rE   rF   r3   rK   rV   r5   r)   r    r   r      s    ; T r)   r   c                        e Zd ZdZdedefdZy)HealthyMajorityPolicya3  
    Policy that returns True if a majority of health check probes are successful.

    Majority means more than half must pass:
    - 3 probes: need 2+ to pass (1 failure allowed)
    - 4 probes: need 3+ to pass (1 failure allowed, tie = unhealthy)
    - 5 probes: need 3+ to pass (2 failures allowed)
    rR   r   c                   K   |j                   }|dz
  dz  }| j                  |       d{   }d}t        |      D ][  }	 |j                  ||       d{   }|s|dz  }|dk  r y||dz
  k  s5t        j                  |j                         d{    ] y7 p7 G# t        $ r}	|	}|dz  }|dk  r|Y d}	~	Sd}	~	ww xY w7 /w)r   r      Nr   FTr7   rY   r   rA   rl   rf   r   r:   )
r   rR   r@   r   allowed_unsuccessful_probesry   last_exceptionr   ro   es
             r    rV   zHealthyMajorityPolicy._execute  s      11 (.za&7#x00V} 	EG)+886JJ/14/2Q6$ !#mmL$C$CDDD!	E$ + 1
 K  )!"+q0+.2(( 3	) Es]   )CBCB BB '
C2"CCCB  	C)B<7C<CCNr   r5   r)   r    r   r      s    ; T r)   r   c                        e Zd ZdZdedefdZy)HealthyAnyPolicyzT
    Policy that returns True if at least one health check probe is successful.
    rR   r   c                 ^  K   |j                   }d}| j                  |       d{   }t        |      D ]R  }	 |j                  ||       d{   }|r y	 ||dz
  k  s,t        j                  |j                         d{    T |r|y7 i7 B# t        $ r}|}Y d}~Kd}~ww xY w7 'w)r   NTr   Fr   )	r   rR   r@   r   r   ry   r   ro   r   s	            r    rV   zHealthyAnyPolicy._execute-  s      11x00V} 
	EG#+886JJ  !#mmL$C$CDDD
	E   % 1 K  #!"# Es\   #B-BB-BBBB-!"B-B+B-B	B(B#B-#B((B-Nr   r5   r)   r    r   r   (  s    ; T r)   r   c                       e Zd ZeZeZeZy)HealthCheckPoliciesN)	rC   rD   rE   r   HEALTHY_ALLr   HEALTHY_MAJORITYr   HEALTHY_ANYr5   r)   r    r   r   J  s    "K,"Kr)   r   DEFAULT_HEALTH_CHECK_POLICYc                       e Zd ZeeefdededefdZe	defd       Z
e	defd       Ze	defd       Zed	edefd
       Zy)AbstractHealthCheckr7   r:   r<   c                 N    |dk  rt        d      || _        || _        || _        y )Nr   z*health_check_probes must be greater than 0)r   _health_check_probes_health_check_delay_health_check_timeout)r   r7   r:   r<   s       r    r#   zAbstractHealthCheck.__init__T  s0     "IJJ$7!#5 %9"r)   r   c                     | j                   S ra   )r   r6   s    r    r7   z'AbstractHealthCheck.health_check_probes`  s    (((r)   c                     | j                   S ra   )r   r6   s    r    r:   z&AbstractHealthCheck.health_check_delayd  s    '''r)   c                     | j                   S ra   )r   r6   s    r    r<   z(AbstractHealthCheck.health_check_timeouth  s    )))r)   r=   c                    K   y wra   r5   r?   s      r    rA   z AbstractHealthCheck.check_healthl  s	     rB   N)rC   rD   rE   DEFAULT_HEALTH_CHECK_PROBESDEFAULT_HEALTH_CHECK_DELAYDEFAULT_HEALTH_CHECK_TIMEOUTrH   rI   r#   rG   r7   r:   r<   r   rJ   rK   rA   r5   r)   r    r   r   S  s     $?$>&B	
: 
: "
: $	
: )S ) ) (E ( ( *e * * 6G D  r)   r   c                        e Zd ZdZdedefdZy)PingHealthCheckz-
    Health check based on PING command.
    r=   r   c                    K   t        |t              r|j                  d       d {   S |j                         }|D ]'  }|j                  j                  d       d {   r' y y7 A7 w)NPINGFT)rk   rz   execute_command	get_nodesredis_connection)r   r@   r=   	all_nodesnodes        r    rA   zPingHealthCheck.check_healthv  sq     i,"226::: "++-I! !!22BB6JJJ !  ;
 Ks'   %A-A)7A-A+ A-%A-+A-N)rC   rD   rE   rF   rJ   rK   rA   r5   r)   r    r   r   q  s    
6G 
D 
r)   r   c                        e Zd ZdZdeeddddddddeeefde	de	de
deeeef      d	ed
ee   dee   deeeef      dee   dee   dee   de	de
de
f fdZdedefdZ xZS )LagAwareHealthCheckz
    Health check available for Redis Enterprise deployments.
    Verify via REST API that the database is healthy based on different lags.
    i$  NTrest_api_portlag_aware_tolerancehttp_timeout
auth_basic
verify_tlsca_fileca_pathca_dataclient_cert_fileclient_key_fileclient_key_passwordr7   r:   r<   c                     t        t        ||t        t               d      |||||	|
|
            | _        || _        || _        t        | !  |||       y)a  
        Initialize LagAwareHealthCheck with the specified parameters.

        Args:
            rest_api_port: Port number for Redis Enterprise REST API (default: 9443)
            lag_aware_tolerance: Tolerance in lag between databases in MS (default: 100)
            http_timeout: Request timeout in seconds (default: DEFAULT_TIMEOUT)
            auth_basic: Tuple of (username, password) for basic authentication
            verify_tls: Whether to verify TLS certificates (default: True)
            ca_file: Path to CA certificate file for TLS verification
            ca_path: Path to CA certificates directory for TLS verification
            ca_data: CA certificate data as string or bytes
            client_cert_file: Path to client certificate file for mutual TLS
            client_key_file: Path to client private key file for mutual TLS
            client_key_password: Password for encrypted client private key
        r   )retries)
re   r   rv   r   r   r   r   r   r   r   )r7   r:   r<   N)	r   r   r   r   _http_client_rest_api_port_lag_aware_tolerancesuperr#   )r   r   r   r   r   r   r   r   r   r   r   r   r7   r:   r<   	__class__s                  r    r#   zLagAwareHealthCheck.__init__  so    H 3$%IK3%!1 /$7
 ,$7! 31!5 	 	
r)   r=   r   c                   K   |j                   t        d      t        |j                  t        t
        f      r|j                  j                         d   }n'|j                  j                         d   j                  }|j                    d| j                   }|| j                  j                  _        d}| j                  j                  d       d{   D ]+  }|d   D ]!  }|d   |k(  r|} |d	   D ]  }||k(  s	|} ! # - | t        j                  d
       t        d      d|d    d| j                   }	| j                  j                  |	d       d{    y7 7 w)a  
        Check database health via Redis Enterprise REST API.

        Note: The client parameter is not used for this health check as it
        relies on the REST API instead of Redis protocol. The client is
        accepted for interface compatibility.
        Nz[Database health check url is not set. Please check DatabaseConfig for the current database.rq   r   :z/v1/bdbs	endpointsdns_nameaddrz8LagAwareHealthCheck failed: Couldn't find a matching bdbzCould not find a matching bdbz	/v1/bdbs/uidz=/availability?extend_check=lag&availability_lag_tolerance_ms=F)expect_jsonT)health_check_urlr   rk   ry   rz   r{   r|   r   rq   r   r   base_urlrx   loggerwarningr   )
r   r@   r=   db_hostr   matching_bdbbdbendpointr   urls
             r    rA   z LagAwareHealthCheck.check_health  s     $$,m  hoo
I'>?oo;;=fEG oo//1!499G//0$2E2E1FG,4  ) **..z:: 
	C, 	J'72#&L %V, Dw'*	
	 NNUV<== U+, -??C?X?X>Y[ 	 ##CU#;;; / ;( 	<s+   CE)E%(E)?A E)E' E)'E))rC   rD   rE   rF   DEFAULT_LAG_AWARE_TOLERANCEr   r   r   r   rH   rI   r   r   strrK   r
   bytesr#   rJ   rA   __classcell__)r   s   @r    r   r     s    "#>-04!%!%/3*.)--1#>$>&B%8
8
 !8
 	8

 U38_-8
 8
 #8
 #8
 %U
+,8
 #3-8
 "#8
 &c]8
  !!8
" "#8
$ $%8
t/6G /D /r)   r   )=rf   r   loggingabcr   r   enumr   typingr   r   r   r	   r
   redis.asyncior   rz   redis.asyncio.clusterr   r}   redis.asyncio.http.http_clientr   r   redis.backoffr   redis.clientr{   redis.clusterr~   redis.http.http_clientr   redis.multidb.exceptionr   redis.retryr   rJ   r$   r(   dictr/   r   DEFAULT_HEALTH_CHECK_INTERVALr   r   r   	getLoggerrC   r   r3   rM   r_   r   r   r   r   r   r   __annotations__r   r   r   r5   r)   r    <module>r      sB      #  5 5 - C R # + : - >  *&778 $ 9 =4 =d =t =    !     " 			8	$$# $N <g 1 gT0 2)5 )X0 D#$ # 4G3R3R 0 R+ <) $o- or)   