o
    <‘ðiçS  ã                   @   sn  d dl Z d dlZd dlmZ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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 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)m*Z*m+Z+ d dl,m-Z- d dl.m/Z/m0Z0m1Z1 d dl2m3Z3 e 4e5¡Z6e3G dd„ de#e"ƒƒZ7de%fdd„Z8G dd„ de#e"ƒZ9G dd„ dƒZ:dS )é    N)ÚAnyÚ	AwaitableÚCallableÚListÚOptionalÚUnion)ÚPubSubHandler)ÚDefaultCommandExecutor)ÚDEFAULT_GRACE_PERIODÚDatabaseConfigÚInitialHealthCheckÚMultiDbConfig)ÚAsyncDatabaseÚDatabaseÚ	Databases)ÚAsyncFailureDetector)ÚHealthCheckÚHealthCheckPolicy)ÚRetry)ÚBackgroundScheduler)Ú	NoBackoff)ÚAsyncCoreCommandsÚAsyncRedisModuleCommands)ÚCircuitBreaker)ÚState)ÚInitialHealthCheckFailedErrorÚNoValidDatabaseExceptionÚUnhealthyDatabaseException)ÚGeoFailoverReason)ÚChannelTÚ
EncodableTÚKeyT)Úexperimentalc                   @   sh  e Zd ZdZdefdd„ZdCdd„Zd	d
„ Zdd„ Zdd„ Z	de
fdd„Zdeddfdd„Z	dDd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d,dd-œd.ed/geeee f f d0ed1e e! d2ed3e e f
d4d5„Z"d6d7„ Z#de$e%ef fd8d9„Z&d:d;„ Z'dedefd<d=„Z(d>e)d?e*d@e*fdAdB„Z+dS )EÚ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g¡ t| j| j| j| j|j|j| j| jd�| _d| _t ¡ | _ t!ƒ | _"|| _#d | _$g | _%d | _&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ÚinitializedÚasyncioÚLockÚ_hc_lockr   Ú_bg_schedulerÚ_configÚ_recurring_hc_taskÚ	_hc_tasksÚ_half_open_state_task)Úselfr$   © rK   ú[/root/parts/websockify/install/lib/python3.10/site-packages/redis/asyncio/multidb/client.pyÚ__init__)   sL   

ÿýÿ
ÿý
ÿýø


zMultiDBClient.__init__rJ   Úreturnc                 Ã   s   �| j s|  ¡ I d H  | S ©N)rA   Ú
initialize©rJ   rK   rK   rL   Ú
__aenter__V   s   €zMultiDBClient.__aenter__c                 Ã   sj   �| j r	| j  ¡  | jr| j ¡  | jD ]}| ¡  q| j ¡ I d H  | jjr3| jjj 	¡ I d H  d S d S rO   )
rG   ÚcancelrI   rH   r5   Úcloser@   Úactive_databaseÚclientÚaclose)rJ   Úhc_taskrK   rK   rL   rW   [   s   €



ÿzMultiDBClient.aclosec                 Ã   ó   �|   ¡ I d H  d S rO   ©rW   ©rJ   Úexc_typeÚ	exc_valueÚ	tracebackrK   rK   rL   Ú	__aexit__k   ó   €zMultiDBClient.__aexit__c                 Ã   s€   �|   ¡ I dH  t | j | j| j¡¡| _d}| jD ]\}}|j	 
| j¡ |j	jtjkr4|s4|| j_d}q|s;tdƒ‚d| _dS )zT
        Perform initialization of databases to define their initial state.
        NFTz4Initial connection failed - no active database found)Ú_perform_initial_health_checkrB   Úcreate_taskrE   Úrun_recurring_asyncr2   Ú_check_databases_healthrG   r-   ÚcircuitÚon_state_changedÚ!_on_circuit_state_change_callbackÚstateÚCBStateÚCLOSEDr@   Ú_active_databaser   rA   )rJ   Úis_active_db_foundÚdatabaseÚweightrK   rK   rL   rP   n   s(   €þÿ€ÿ
zMultiDBClient.initializec                 C   s   | j S )zE
        Returns a sorted (by weight) list of all databases.
        )r-   rQ   rK   rK   rL   Úget_databases’   s   zMultiDBClient.get_databasesrm   Nc                 Ã   s†   �d}| j D ]\}}||krd} nq|stdƒ‚|  |¡I dH  |jjtjkr?| j  d¡d \}}| j 	|t
j¡I dH  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-   Ú
ValueErrorÚ_check_db_healthre   rh   ri   rj   Ú	get_top_nr@   Úset_active_databaser   ÚMANUALr   )rJ   rm   ÚexistsÚexisting_dbÚ_Úhighest_weighted_dbrK   rK   rL   ru   ˜   s&   €þ
ÿÿz!MultiDBClient.set_active_databaseTÚskip_initial_health_checkc                 Ã   s  �|j  dtdtƒ d�i¡ |jr| jjj|jfi |j ¤Ž}n"|jr7|j tdtƒ d�¡ | jjj|jd�}n
| jjdi |j ¤Ž}|j	du rJ| 
¡ n|j	}t|||j|jd�}z
|  |¡I dH  W n tym   |sk‚ Y nw | j d¡d \}}| j ||j¡ |  ||¡I dH  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.
        Úretryr   )ÚretriesÚbackoff)Úconnection_poolN)rV   re   rn   Úhealth_check_urlrq   rK   )Úclient_kwargsÚupdater   r   Úfrom_urlrF   Úclient_classÚ	from_poolÚ	set_retryre   Údefault_circuit_breakerr   rn   r€   rs   r   r-   rt   ÚaddÚ_change_active_database)rJ   r$   r{   rV   re   rm   rz   Úhighest_weightrK   rK   rL   Úadd_database³   sD   €ÿÿÿ
ÿýüÿÿzMultiDBClient.add_databaseÚnew_databaseÚhighest_weight_databasec                 Ã   s>   �|j |j kr|jjtjkr| j |tj¡I d H  d S d S d S rO   )	rn   re   rh   ri   rj   r@   ru   r   Ú	AUTOMATIC)rJ   rŒ   r�   rK   rK   rL   r‰   ä   s   €ÿýz%MultiDBClient._change_active_databasec                 Ã   sZ   �| j  |¡}| j  d¡d \}}||kr)|jjtjkr+| j |t	j
¡I dH  dS dS dS )z<
        Removes a database from the database list.
        rq   r   N)r-   Úremovert   re   rh   ri   rj   r@   ru   r   rv   )rJ   rm   rn   rz   rŠ   rK   rK   rL   Úremove_databaseï   s   €ÿýzMultiDBClient.remove_databasern   c                 Ã   sp   �d}| j D ]\}}||krd} nq|stdƒ‚| j  d¡d \}}| j  ||¡ ||_|  ||¡I dH  dS )z<
        Updates a database from the database list.
        NTrp   rq   r   )r-   rr   rt   Úupdate_weightrn   r‰   )rJ   rm   rn   rw   rx   ry   rz   rŠ   rK   rK   rL   Ú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)r7   Úappend)rJ   r“   rK   rK   rL   Úadd_failure_detector  s   z"MultiDBClient.add_failure_detectorÚhealthcheckc              	   Ã   sN   �| j 4 I dH š | j |¡ W d  ƒI dH  dS 1 I dH s w   Y  dS )z:
        Adds a new health check to the database.
        N)rD   r0   r”   )rJ   r–   rK   rK   rL   Úadd_health_check  s   €.ÿzMultiDBClient.add_health_checkc                 Ï   s.   �| j s|  ¡ I dH  | jj|i |¤ŽI dH S )zB
        Executes a single command and return its result.
        N)rA   rP   r@   Úexecute_command©rJ   ÚargsÚoptionsrK   rK   rL   r˜     s   €zMultiDBClient.execute_commandc                 C   s   t | ƒS )z:
        Enters into pipeline mode of the client.
        )ÚPipelinerQ   rK   rK   rL   Úpipeline'  s   zMultiDBClient.pipelineF©Ú
shard_hintÚvalue_from_callableÚwatch_delayÚfuncrœ   ÚwatchesrŸ   r    r¡   c                Ç   s:   �| j s|  ¡ I dH  | jj|g|¢R |||dœŽI dH S )z3
        Executes callable as transaction.
        Nrž   )rA   rP   r@   Úexecute_transaction)rJ   r¢   rŸ   r    r¡   r£   rK   rK   rL   Útransaction-  s   €ÿþûzMultiDBClient.transactionc                 Ë   s&   �| j s|  ¡ I dH  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.
        N)rA   rP   ÚPubSub)rJ   ÚkwargsrK   rK   rL   ÚpubsubC  s   €zMultiDBClient.pubsubc                 ƒ   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 rK   rK   )Ú.0ÚtaskÚresult©Ú
task_to_dbrK   rL   Ú
<dictcomp>^  s    ÿz9MultiDBClient._check_databases_health.<locals>.<dictcomp>z%Health check failed, due to exception)Úexc_infoF)rH   r-   rB   rb   rs   r”   ÚgatherÚzipÚitemsÚ
isinstancer   rm   ri   ÚOPENre   rh   ÚloggerÚdebugÚoriginal_exception)rJ   rm   ry   r«   ÚresultsÚ
db_resultsr¬   Úunhealthy_dbrK   r­   rL   rd   N  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: )rd   rF   Úinitial_health_check_policyr   ÚALL_AVAILABLEÚvaluesÚMAJORITY_AVAILABLEÚsumÚlenÚONE_AVAILABLEr   )rJ   r¹   Ú
is_healthyrK   rK   rL   ra   p  s    €ÿÿÿz+MultiDBClient._perform_initial_health_checkc                 Ã   sX   �| j  | j|¡I dH }|s|jjtjkrtj|j_|S |r*|jjtjkr*tj|j_|S )zO
        Runs health checks on the given database until first failure.
        N)r5   Úexecuter0   re   rh   ri   rµ   rj   )rJ   rm   rÄ   rK   rK   rL   rs   ˆ  s   €
ÿ

zMultiDBClient._check_db_healthre   Ú	old_stateÚ	new_statec                 C   s”   t  ¡ }|tjkrt  |  |j¡¡| _d S |tjkr0|tj	kr0t
 d|j› d�¡ | tt|¡ |tjkrF|tjkrHt
 d|j› d�¡ d S d S d S )Nz	Database z- is unreachable. Failover has been initiated.z is reachable again.)rB   Úget_running_loopri   Ú	HALF_OPENrb   rs   rm   rI   rj   rµ   r¶   ÚwarningÚ
call_laterr
   Ú_half_open_circuitÚinfo)rJ   re   rÆ   rÇ   ÚlooprK   rK   rL   rg   š  s   

ÿÿÿz/MultiDBClient._on_circuit_state_change_callback)rJ   r#   rN   r#   )T),Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   rM   rR   rW   r_   rP   r   ro   r   ru   r   Úboolr‹   r‰   r�   Úfloatr’   r   r•   r   r—   r˜   r�   r   r   r   r   r!   r   Ústrr¥   r¨   Údictr   rd   ra   rs   r   ri   rg   rK   rK   rK   rL   r#   "   sf    
-$ÿÿ
ÿ1ÿ
ÿ	
úþýüû
ú"ÿÿÿr#   re   c                 C   s   t j| _d S rO   )ri   rÉ   rh   )re   rK   rK   rL   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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.
    rV   c                 C   s   g | _ || _d S rO   )Ú_command_stackÚ_client)rJ   rV   rK   rK   rL   rM   ¸  s   
zPipeline.__init__rJ   rN   c                 Ã   ó   �| S rO   rK   rQ   rK   rK   rL   rR   ¼  ó   €zPipeline.__aenter__c                 Ã   s*   �|   ¡ I d H  | j |||¡I d H  d S rO   )ÚresetrÙ   r_   r[   rK   rK   rL   r_   ¿  s   €zPipeline.__aexit__c                 C   s   |   ¡  ¡ S rO   )Ú_async_selfÚ	__await__rQ   rK   rK   rL   rÞ   Ã  r×   zPipeline.__await__c                 Ã   rÚ   rO   rK   rQ   rK   rK   rL   rÝ   Æ  rÛ   zPipeline._async_selfc                 C   s
   t | jƒS rO   )rÂ   rØ   rQ   rK   rK   rL   Ú__len__É  s   
zPipeline.__len__c                 C   s   dS )z1Pipeline instances should always evaluate to TrueTrK   rQ   rK   rK   rL   Ú__bool__Ì  s   zPipeline.__bool__Nc                 Ã   s   �g | _ d S rO   )rØ   rQ   rK   rK   rL   rÜ   Ð  s   €
zPipeline.resetc                 Ã   s   �|   ¡ I dH  dS )zClose the pipelineN)rÜ   rQ   rK   rK   rL   rW   Ó  s   €zPipeline.aclosec                 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™   rK   rK   rL   Úpipeline_execute_command×  s   z!Pipeline.pipeline_execute_commandc                 O   s   | j |i |¤ŽS )zAdds a command to the stack)rá   ©rJ   rš   r§   rK   rK   rL   r˜   æ  s   zPipeline.execute_commandc                 Ã   sV   �| j js| j  ¡ I dH  z| j j t| jƒ¡I dH W |  ¡ I dH  S |  ¡ I dH  w )z0Execute all the commands in the current pipelineN)rÙ   rA   rP   r@   Úexecute_pipelineÚtuplerØ   rÜ   rQ   rK   rK   rL   rÅ   ê  s   €
ÿ zPipeline.execute)rJ   rœ   rN   rœ   ©rN   N)rN   rœ   )rÏ   rÐ   rÑ   rÒ   r#   rM   rR   r_   rÞ   rÝ   Úintrß   rÓ   rà   rÜ   rW   rá   r˜   r   r   rÅ   rK   rK   rK   rL   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„ Z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defdd„Zdd„ Z	d(de
dee fdd „Zdd!d"œd#eddfd$d%„ZdS ))r¦   z2
    PubSub object for multi database client.
    rV   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
        NrK   )rÙ   r@   r¨   )rJ   rV   r§   rK   rK   rL   rM   ü  s   zPubSub.__init__rN   c                 Ã   rÚ   rO   rK   rQ   rK   rK   rL   rR     rÛ   zPubSub.__aenter__Nc                 Ã   rY   rO   rZ   r[   rK   rK   rL   r_   
  r`   zPubSub.__aexit__c                 Ã   s   �| j j d¡I d H S )NrW   ©rÙ   r@   Úexecute_pubsub_methodrQ   rK   rK   rL   rW     s   €zPubSub.aclosec                 C   s   | j jjjS rO   )rÙ   r@   Úactive_pubsubÚ
subscribedrQ   rK   rK   rL   rê     s   zPubSub.subscribedrš   c                 Ç   s   �| j jjdg|¢R Ž I d H S )Nr˜   rç   ©rJ   rš   rK   rK   rL   r˜     s   €ÿÿzPubSub.execute_commandr§   c                 Ï   ó$   �| j jjdg|¢R i |¤ŽI dH 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()``.
        Ú
psubscribeNrç   râ   rK   rK   rL   rí     ó   €ÿÿÿzPubSub.psubscribec                 Ç   ó   �| j jjdg|¢R Ž I dH S )zj
        Unsubscribe from the supplied patterns. If empty, unsubscribe from
        all patterns.
        ÚpunsubscribeNrç   rë   rK   rK   rL   rð   %  ó   €ÿÿzPubSub.punsubscribec                 Ï   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()``.
        Ú	subscribeNrç   râ   rK   rK   rL   rò   .  rî   zPubSub.subscribec                 Ç   rï   )zi
        Unsubscribe from the supplied channels. If empty, unsubscribe from
        all channels
        ÚunsubscribeNrç   rë   rK   rK   rL   ró   :  rñ   zPubSub.unsubscribeFç        Úignore_subscribe_messagesÚtimeoutc                 Ã   s   �| j jjd||d�I dH 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ö   Nrç   )rJ   rõ   rö   rK   rK   rL   r÷   C  s   €
ýzPubSub.get_messageg      ð?)Úexception_handlerÚpoll_timeoutrù   c                Ã   s   �| j jj||| d�I dH S )a   Process pub/sub messages using registered callbacks.

        This is the equivalent of :py:meth:`redis.PubSub.run_in_thread` in
        redis-py, but it is a coroutine. To launch it as a separate task, use
        ``asyncio.create_task``:

            >>> task = asyncio.create_task(pubsub.run())

        To shut it down, use asyncio cancellation:

            >>> task.cancel()
            >>> await task
        )Ú
sleep_timerø   r¨   N)rÙ   r@   Úexecute_pubsub_run)rJ   rø   rù   rK   rK   rL   ÚrunS  s   €ÿz
PubSub.run)rN   r¦   rå   )Frô   )rÏ   rÐ   rÑ   rÒ   r#   rM   rR   r_   rW   ÚpropertyrÓ   rê   r    r˜   r   r   rí   rð   r   rò   ró   r   rÔ   r÷   rü   rK   rK   rK   rL   r¦   ÷  s4    

	
ÿÿ
ÿüüûr¦   );rB   ÚloggingÚtypingr   r   r   r   r   r   Úredis.asyncio.clientr   Ú&redis.asyncio.multidb.command_executorr	   Úredis.asyncio.multidb.configr
   r   r   r   Úredis.asyncio.multidb.databaser   r   r   Ú&redis.asyncio.multidb.failure_detectorr   Ú!redis.asyncio.multidb.healthcheckr   r   Úredis.asyncio.retryr   Úredis.backgroundr   Úredis.backoffr   Úredis.commandsr   r   Úredis.multidb.circuitr   r   ri   Úredis.multidb.exceptionr   r   r   Úredis.observability.attributesr   Úredis.typingr   r    r!   Úredis.utilsr"   Ú	getLoggerrÏ   r¶   r#   rÌ   rœ   r¦   rK   rK   rK   rL   Ú<module>   s8     
   D