o
    <‘ði}Y  ã                   @   sf  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
 d dl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mZmZm Z  d dl!m"Z"m#Z#m$Z$ d dl%m&Z&m'Z'm(Z( d dl)m*Z* d dl+m,Z, d dl-m.Z. d dl/m0Z0 e 1e2¡Z3e0G dd„ deeƒƒZ4defdd„Z5G dd„ deeƒZ6G dd„ dƒZ7dS )é    N)ÚAnyÚCallableÚListÚOptional)ÚHealthCheckÚHealthCheckPolicy)ÚBackgroundScheduler)Ú	NoBackoff)ÚPubSubWorkerThread)ÚCoreCommandsÚRedisModuleCommands)ÚMaintNotificationsConfig)ÚCircuitBreaker)ÚState)ÚDefaultCommandExecutor)ÚDEFAULT_GRACE_PERIODÚDatabaseConfigÚInitialHealthCheckÚMultiDbConfig)ÚDatabaseÚ	DatabasesÚSyncDatabase)ÚInitialHealthCheckFailedErrorÚNoValidDatabaseExceptionÚUnhealthyDatabaseException)ÚFailureDetector)ÚGeoFailoverReason)ÚRetry)Úexperimentalc                   @   s0  e Zd ZdZdefdd„Zdd„ Zdd„ Zd	efd
d„Z	de
d	dfdd„Z	d:dedefdd„Zde
de
fdd„Zdefdd„Zde
defdd„Zdefdd„Zd efd!d"„Zd#d$„ Zd%d&„ Zd'ed(gdf fd)d*„Zd+d,„ Zde
d	efd-d.„Zd	eeef fd/d0„Zd1d2„ Z d3e!d4e"d5e"fd6d7„Z#d8d9„ Z$dS );ÚMultiDBClientzŠ
    Client that operates on multiple logical Redis databases.
    Should be used in Client-side geographic failover database setups.
    Úconfigc              
   C   sä   |  ¡ | _|js| ¡ n|j| _|j| _|j ¡ | _	|j
s!| ¡ n|j
| _|jd u r.| ¡ n|j| _| j | j¡ |j| _|j| _|j| _| j tf¡ t| j| j| j| j|j|j| j| jd�| _d| _tƒ | _t  !¡ | _"|| _#d S )N)Úfailure_detectorsÚ	databasesÚcommand_retryÚfailover_strategyÚfailover_attemptsÚfailover_delayÚevent_dispatcherÚauto_fallback_intervalF)$r"   Ú
_databasesÚhealth_checksÚdefault_health_checksÚ_health_checksÚhealth_check_intervalÚ_health_check_intervalÚhealth_check_policyÚvalueÚ_health_check_policyr!   Údefault_failure_detectorsÚ_failure_detectorsr$   Údefault_failover_strategyÚ_failover_strategyÚset_databasesr(   Ú_auto_fallback_intervalr'   Ú_event_dispatcherr#   Ú_command_retryÚupdate_supported_errorsÚConnectionRefusedErrorr   r%   r&   Úcommand_executorÚinitializedr   Ú_bg_schedulerÚ	threadingÚLockÚ_hc_lockÚ_config)Úselfr    © rD   úS/root/parts/websockify/install/lib/python3.10/site-packages/redis/multidb/client.pyÚ__init__*   sF   

ÿýÿ
ÿý
ÿýø


zMultiDBClient.__init__c                 C   ó$   z|   ¡  W d S  ty   Y d S w ©N)ÚcloseÚ	Exception©rC   rD   rD   rE   Ú__del__T   s
   üzMultiDBClient.__del__c                 C   sv   | j  | j¡ | j  | j| j¡ d}| jD ]\}}|j | j	¡ |jj
tjkr/|s/|| j_d}q|s6tdƒ‚d| _dS )zT
        Perform initialization of databases to define their initial state.
        FTz4Initial connection failed - no active database foundN)r>   Úrun_coro_syncÚ_perform_initial_health_checkÚrun_recurring_coror.   Ú_check_databases_healthr)   ÚcircuitÚon_state_changedÚ!_on_circuit_state_change_callbackÚstateÚCBStateÚCLOSEDr<   Ú_active_databaser   r=   )rC   Úis_active_db_foundÚdatabaseÚweightrD   rD   rE   Ú
initialize]   s"   	þ€ÿ
zMultiDBClient.initializeÚreturnc                 C   s   | j S )zE
        Returns a sorted (by weight) list of all databases.
        )r)   rK   rD   rD   rE   Úget_databasesƒ   s   zMultiDBClient.get_databasesrY   Nc                 C   s|   d}| j D ]\}}||krd} nq|stdƒ‚| j | j|¡ |jjtjkr:| j  	d¡d \}}|t
jf| j_dS tdƒ‚)zL
        Promote one of the existing databases to become an active.
        NTú/Given database is not a member of database listé   r   z1Cannot set active database, database is unhealthy)r)   Ú
ValueErrorr>   rM   Ú_check_db_healthrQ   rT   rU   rV   Ú	get_top_nr   ÚMANUALr<   Úactive_databaser   )rC   rY   ÚexistsÚexisting_dbÚ_Úhighest_weighted_dbrD   rD   rE   Úset_active_database‰   s$   þþÿz!MultiDBClient.set_active_databaseTÚskip_initial_health_checkc                 C   s"  t dtƒ d�|jd< d|jvrtdd�|jd< |jr(| jjj|jfi |j¤Ž}n"|jr@|j t dtƒ d�¡ | jjj|jd�}n
| jjdi |j¤Ž}|j	du rS| 
¡ n|j	}t|||j|jd	�}z
| j | j|¡ W n tyv   |st‚ Y nw | j d
¡d \}}| j ||j¡ |  ||¡ dS )zù
        Adds a new database to the database list.

        Args:
            config: DatabaseConfig object that contains the database configuration.
            skip_initial_health_check: If True, adds the database even if it is unhealthy.
        r   )ÚretriesÚbackoffÚretryÚmaint_notifications_configF)Úenabled)Úconnection_poolN)ÚclientrQ   rZ   Úhealth_check_urlr_   rD   )r   r	   Úclient_kwargsr   Úfrom_urlrB   Úclient_classÚ	from_poolÚ	set_retryrQ   Údefault_circuit_breakerr   rZ   rr   r>   rM   ra   r   r)   rb   ÚaddÚ_change_active_database)rC   r    rj   rq   rQ   rY   rh   Úhighest_weightrD   rD   rE   Úadd_database¥   sH   
ÿÿÿÿ
ÿýüÿÿzMultiDBClient.add_databaseÚnew_databaseÚhighest_weight_databasec                 C   s4   |j |j kr|jjtjkr|tjf| j_d S d S d S rH   )	rZ   rQ   rT   rU   rV   r   Ú	AUTOMATICr<   rd   )rC   r}   r~   rD   rD   rE   rz   Ý   s   þýz%MultiDBClient._change_active_databasec                 C   sP   | j  |¡}| j  d¡d \}}||kr$|jjtjkr&|tjf| j	_
dS dS dS )z<
        Removes a database from the database list.
        r_   r   N)r)   Úremoverb   rQ   rT   rU   rV   r   rc   r<   rd   )rC   rY   rZ   rh   r{   rD   rD   rE   Úremove_databaseé   s   þýzMultiDBClient.remove_databaserZ   c                 C   sh   d}| j D ]\}}||krd} nq|stdƒ‚| j  d¡d \}}| j  ||¡ ||_|  ||¡ dS )z<
        Updates a database from the database list.
        NTr^   r_   r   )r)   r`   rb   Úupdate_weightrZ   rz   )rC   rY   rZ   re   rf   rg   rh   r{   rD   rD   rE   Úupdate_database_weightù   s   þz$MultiDBClient.update_database_weightÚfailure_detectorc                 C   s   | j  |¡ dS )z>
        Adds a new failure detector to the database.
        N)r3   Úappend)rC   r„   rD   rD   rE   Úadd_failure_detector  s   z"MultiDBClient.add_failure_detectorÚhealthcheckc                 C   s8   | j � | j |¡ W d  ƒ dS 1 sw   Y  dS )z:
        Adds a new health check to the database.
        N)rA   r,   r…   )rC   r‡   rD   rD   rE   Úadd_health_check  s   "ÿzMultiDBClient.add_health_checkc                 O   s    | j s|  ¡  | jj|i |¤ŽS )zB
        Executes a single command and return its result.
        )r=   r[   r<   Úexecute_command©rC   ÚargsÚoptionsrD   rD   rE   r‰     s   zMultiDBClient.execute_commandc                 C   s   t | ƒS )z:
        Enters into pipeline mode of the client.
        )ÚPipelinerK   rD   rD   rE   Úpipeline"  s   zMultiDBClient.pipelineÚfuncr�   c                 O   s&   | j s|  ¡  | jj|g|¢|¢R Ž S )z3
        Executes callable as transaction.
        )r=   r[   r<   Úexecute_transaction)rC   r�   ÚwatchesrŒ   rD   rD   rE   Útransaction(  s   zMultiDBClient.transactionc                 K   s   | j s|  ¡  t| fi |¤ŽS )z¨
        Return a Publish/Subscribe object. With this object, you can
        subscribe to channels and listen for messages that get published to
        them.
        )r=   r[   ÚPubSub)rC   ÚkwargsrD   rD   rE   Úpubsub1  s   zMultiDBClient.pubsubc                 Ã   s†   �| j � t| jƒ}W d  ƒ n1 sw   Y  | j ||¡I dH }|s3|jjtjkr1tj|j_|S |rA|jjtj	krAtj	|j_|S )zO
        Runs health checks on the given database until first failure.
        N)
rA   Úlistr,   r1   ÚexecuterQ   rT   rU   ÚOPENrV   )rC   rY   r*   Ú
is_healthyrD   rD   rE   ra   <  s   €ÿ

zMultiDBClient._check_db_healthc                 ƒ   sº   �i ‰ g | _ | jD ]\}}t |  |¡¡}|ˆ |< | j  |¡ q	tj| j ddiŽI dH }‡ fdd„t| j |ƒD ƒ}| ¡ D ]\}}t	|t
ƒrZ|j}tj|j_tjd|jd� d||< q=|S )	zk
        Runs health checks as a recurring task.
        Runs health checks against all databases.
        Úreturn_exceptionsTNc                    s   i | ]	\}}ˆ | |“qS rD   rD   )Ú.0ÚtaskÚresult©Ú
task_to_dbrD   rE   Ú
<dictcomp>_  s    ÿz9MultiDBClient._check_databases_health.<locals>.<dictcomp>z%Health check failed, due to exception)Úexc_infoF)Ú	_hc_tasksr)   ÚasyncioÚcreate_taskra   r…   ÚgatherÚzipÚitemsÚ
isinstancer   rY   rU   r˜   rQ   rT   ÚloggerÚdebugÚoriginal_exception)rC   rY   rg   rœ   ÚresultsÚ
db_resultsr�   Úunhealthy_dbrD   rž   rE   rP   O  s,   €

ÿ

þ€z%MultiDBClient._check_databases_healthc                 Ã   sŒ   �|   ¡ I dH }d}| jjtjkrd| ¡ v}n!| jjtjkr,t| ¡ ƒt|ƒd k}n| jjtj	kr9d| ¡ v }|sDt
d| jj› �ƒ‚dS )zj
        Runs initial health check and evaluate healthiness based on initial_health_check_policy.
        NTFé   z:Initial health check failed. Initial health check policy: )rP   rB   Úinitial_health_check_policyr   ÚALL_AVAILABLEÚvaluesÚMAJORITY_AVAILABLEÚsumÚlenÚONE_AVAILABLEr   )rC   r¬   r™   rD   rD   rE   rN   q  s    €ÿÿÿz+MultiDBClient._perform_initial_health_checkrQ   Ú	old_stateÚ	new_statec                 C   sŒ   |t jkr| j | j|j¡ d S |t jkr,|t jkr,t 	d|j› d�¡ | j 
tt|¡ |t jkrB|t jkrDt d|j› d�¡ d S d S d S )Nz	Database z- is unreachable. Failover has been initiated.z is reachable again.)rU   Ú	HALF_OPENr>   Úrun_coro_fire_and_forgetra   rY   rV   r˜   r©   ÚwarningÚrun_oncer   Ú_half_open_circuitÚinfo)rC   rQ   r·   r¸   rD   rD   rE   rS   ‰  s   
ÿÿÿÿz/MultiDBClient._on_circuit_state_change_callbackc                 C   sV   | j rz
| j  | jj¡ W n	 ty   Y nw | j  ¡  | jjr)| jjj ¡  dS dS )z:
        Closes the client and all its resources.
        N)	r>   rM   r1   rI   rJ   Ústopr<   rd   rq   rK   rD   rD   rE   rI   ž  s   ÿ
ÿzMultiDBClient.close)T)%Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   rF   rL   r[   r   r]   r   ri   r   Úboolr|   rz   r   r�   Úfloatrƒ   r   r†   r   rˆ   r‰   rŽ   r   r’   r•   ra   ÚdictrP   rN   r   rU   rS   rI   rD   rD   rD   rE   r   #   sJ    *	&ÿÿ
ÿ8ÿ
ÿ		"ÿÿ
ÿr   rQ   c                 C   s   t j| _d S rH   )rU   r¹   rT   )rQ   rD   rD   rE   r½   ±  ó   r½   c                   @   sŒ   e Zd ZdZdefdd„Zddd„Zdd	„ Zd
d„ Zde	fdd„Z
defdd„Zddd„Zddd„Zddd„Zdd„ Zdee fdd„ZdS )r�   zG
    Pipeline implementation for multiple logical Redis databases.
    rq   c                 C   s   g | _ || _d S rH   )Ú_command_stackÚ_client)rC   rq   rD   rD   rE   rF   º  s   
zPipeline.__init__r\   c                 C   ó   | S rH   rD   rK   rD   rD   rE   Ú	__enter__¾  ó   zPipeline.__enter__c                 C   ó   |   ¡  d S rH   ©Úreset)rC   Úexc_typeÚ	exc_valueÚ	tracebackrD   rD   rE   Ú__exit__Á  rÇ   zPipeline.__exit__c                 C   rG   rH   ©rÏ   rJ   rK   rD   rD   rE   rL   Ä  s
   ÿzPipeline.__del__c                 C   s
   t | jƒS rH   )rµ   rÈ   rK   rD   rD   rE   Ú__len__Ê  ó   
zPipeline.__len__c                 C   s   dS )z1Pipeline instances should always evaluate to TrueTrD   rK   rD   rD   rE   Ú__bool__Í  s   zPipeline.__bool__Nc                 C   s
   g | _ d S rH   )rÈ   rK   rD   rD   rE   rÏ   Ñ  rÖ   zPipeline.resetc                 C   s   |   ¡  dS )zClose the pipelineNrÎ   rK   rD   rD   rE   rI   Ô  ó   zPipeline.closec                 O   s   | j  ||f¡ | S )ar  
        Stage a command to be executed when execute() is next called

        Returns the current Pipeline object back so commands can be
        chained together, such as:

        pipe = pipe.set('foo', 'bar').incr('baz').decr('bang')

        At some other point, you can then run: pipe.execute(),
        which will execute all commands queued in the pipe.
        )rÈ   r…   rŠ   rD   rD   rE   Úpipeline_execute_commandØ  s   z!Pipeline.pipeline_execute_commandc                 O   s   | j |i |¤ŽS )zAdds a command to the stack)rÙ   ©rC   r‹   r”   rD   rD   rE   r‰   ç  s   zPipeline.execute_commandc                 C   s<   | j js	| j  ¡  z| j j t| jƒ¡W |  ¡  S |  ¡  w )z0Execute all the commands in the current pipeline)rÉ   r=   r[   r<   Úexecute_pipelineÚtuplerÈ   rÏ   rK   rD   rD   rE   r—   ë  s   
ÿzPipeline.execute)r\   r�   ©r\   N)rÀ   rÁ   rÂ   rÃ   r   rF   rË   rÓ   rL   ÚintrÕ   rÄ   r×   rÏ   rI   rÙ   r‰   r   r   r—   rD   rD   rD   rE   r�   µ  s    



r�   c                   @   sè   e Zd ZdZdefdd„Zd.dd„Zd/d	d
„Zd/dd„Zd/dd„Z	e
defdd„ƒZdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Z	 d0d!ed"efd#d$„Z	 d0d!ed"efd%d&„Z	 			d1d'ed(ed)ee d*edd+f
d,d-„ZdS )2r“   z2
    PubSub object for multi database client.
    rq   c                 K   s   || _ | j jjdi |¤Ž dS )zýInitialize the PubSub object for a multi-database client.

        Args:
            client: MultiDBClient instance to use for pub/sub operations
            **kwargs: Additional keyword arguments to pass to the underlying pubsub implementation
        NrD   )rÉ   r<   r•   )rC   rq   r”   rD   rD   rE   rF   ý  s   zPubSub.__init__r\   c                 C   rÊ   rH   rD   rK   rD   rD   rE   rË     rÌ   zPubSub.__enter__Nc                 C   rG   rH   rÔ   rK   rD   rD   rE   rL     s
   ÿzPubSub.__del__c                 C   s   | j j d¡S )NrÏ   ©rÉ   r<   Úexecute_pubsub_methodrK   rD   rD   rE   rÏ     s   zPubSub.resetc                 C   rÍ   rH   rÎ   rK   rD   rD   rE   rI     rÇ   zPubSub.closec                 C   s   | j jjjS rH   )rÉ   r<   Úactive_pubsubÚ
subscribedrK   rD   rD   rE   râ     rØ   zPubSub.subscribedc                 G   ó   | j jjdg|¢R Ž S )Nr‰   rß   ©rC   r‹   rD   rD   rE   r‰     s
   ÿÿzPubSub.execute_commandc                 O   ó   | j jjdg|¢R i |¤ŽS )aE  
        Subscribe to channel patterns. Patterns supplied as keyword arguments
        expect a pattern name as the key and a callable as the value. A
        pattern's callable will be invoked automatically when a message is
        received on that pattern rather than producing a message via
        ``listen()``.
        Ú
psubscriberß   rÚ   rD   rD   rE   ræ   #  ó   ÿÿÿzPubSub.psubscribec                 G   rã   )zj
        Unsubscribe from the supplied patterns. If empty, unsubscribe from
        all patterns.
        Úpunsubscriberß   rä   rD   rD   rE   rè   /  ó
   ÿÿzPubSub.punsubscribec                 O   rå   )aR  
        Subscribe to channels. Channels supplied as keyword arguments expect
        a channel name as the key and a callable as the value. A channel's
        callable will be invoked automatically when a message is received on
        that channel rather than producing a message via ``listen()`` or
        ``get_message()``.
        Ú	subscriberß   rÚ   rD   rD   rE   rê   8  rç   zPubSub.subscribec                 G   rã   )zi
        Unsubscribe from the supplied channels. If empty, unsubscribe from
        all channels
        Úunsubscriberß   rä   rD   rD   rE   rë   D  s   zPubSub.unsubscribec                 O   rå   )az  
        Subscribes the client to the specified shard channels.
        Channels supplied as keyword arguments expect a channel name as the key
        and a callable as the value. A channel's callable will be invoked automatically
        when a message is received on that channel rather than producing a message via
        ``listen()`` or ``get_sharded_message()``.
        Ú
ssubscriberß   rÚ   rD   rD   rE   rì   K  rç   zPubSub.ssubscribec                 G   rã   )zu
        Unsubscribe from the supplied shard_channels. If empty, unsubscribe from
        all shard_channels
        Úsunsubscriberß   rä   rD   rD   rE   rí   W  ré   zPubSub.sunsubscribeFç        Úignore_subscribe_messagesÚtimeoutc                 C   ó   | j jjd||d�S )a  
        Get the next message if one is available, otherwise None.

        If timeout is specified, the system will wait for `timeout` seconds
        before returning. Timeout should be specified as a floating point
        number, or None, to wait indefinitely.
        Úget_message©rï   rð   rß   ©rC   rï   rð   rD   rD   rE   rò   `  ó
   
ýzPubSub.get_messagec                 C   rñ   )a&  
        Get the next message if one is available in a sharded channel, otherwise None.

        If timeout is specified, the system will wait for `timeout` seconds
        before returning. Timeout should be specified as a floating point
        number, or None, to wait indefinitely.
        Úget_sharded_messageró   rß   rô   rD   rD   rE   rö   p  rõ   zPubSub.get_sharded_messageÚ
sleep_timeÚdaemonÚexception_handlerÚsharded_pubsubr
   c                 C   s   | j jj|||| |d�S )N)rø   rù   r•   rú   )rÉ   r<   Úexecute_pubsub_run)rC   r÷   rø   rù   rú   rD   rD   rE   Úrun_in_thread€  s   ûzPubSub.run_in_thread)r\   r“   rÝ   )Frî   )rî   FNF)rÀ   rÁ   rÂ   rÃ   r   rF   rË   rL   rÏ   rI   ÚpropertyrÄ   râ   r‰   ræ   rè   rê   rë   rì   rí   rÅ   rò   rö   r   r   rü   rD   rD   rD   rE   r“   ø  sV    


	
	
ÿÿ
ÿÿÿ
ÿûþýüûúr“   )8r£   Úloggingr?   Útypingr   r   r   r   Ú!redis.asyncio.multidb.healthcheckr   r   Úredis.backgroundr   Úredis.backoffr	   Úredis.clientr
   Úredis.commandsr   r   Úredis.maint_notificationsr   Úredis.multidb.circuitr   r   rU   Úredis.multidb.command_executorr   Úredis.multidb.configr   r   r   r   Úredis.multidb.databaser   r   r   Úredis.multidb.exceptionr   r   r   Úredis.multidb.failure_detectorr   Úredis.observability.attributesr   Úredis.retryr   Úredis.utilsr   Ú	getLoggerrÀ   r©   r   r½   r�   r“   rD   rD   rD   rE   Ú<module>   s:    
   C