~fzk :ddlZddlZddlZddlZddlZddlmZddlmZm Z m Z m Z m Z m Z mZddlmZmZddlmZddlmZddlmZmZmZddlmZmZdd lmZdd lm Z m!Z!m"Z"dd l#m$Z$m%Z%dd l&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5dd l6m7Z7ddl8m9Z9ddl:m;Z;mZ>m?Z?m@Z@deAdeeAeBfdeAfdZCdZDdZEdZFdedede e eBeBfe eAefffdZGdZHdZIdZJdZKdZLdZMd ZNd!ZOGd"d#e!ZPGd$d%ZQGd&d'eQeZRGd(d)ZSGd*d+ZTGd,d-ZUGd.d/eZVGd0d1eRZWd2eAde d3effd4ZXd5ZYeYD]2ZZeZjd6d7jZZe]eWeZeXeZ4Gd8d9Z^Gd:d;Z_y)<N) OrderedDict)AnyCallableDictListOptionalTupleUnion)CommandsParserEncoder) parse_scan)default_backoff)CaseInsensitiveDictPubSubRedis) READ_COMMANDSRedisClusterCommands) list_or_args)ConnectionPool DefaultParser parse_url)REDIS_CLUSTER_HASH_SLOTSkey_slot)AskErrorAuthenticationErrorClusterCrossSlotErrorClusterDownError ClusterErrorConnectionError DataErrorMasterDownError MovedErrorRedisClusterException RedisError ResponseErrorSlotNotCoveredError TimeoutError TryAgainErrorLock)Retry)HIREDIS_AVAILABLE dict_mergelist_keys_to_dict merge_resultsafe_str str_if_byteshostportreturnc|d|S)N:r2r3s V/var/lib/jenkins/workspace/mettalog/venv/lib/python3.12/site-packages/redis/cluster.py get_node_namer:.sV1TF c^|jxs |jj|dfi|S)Nr) connectionconnection_poolget_connection) redis_nodeargsoptionss r9r?r?2s:   $MJ$>$>$M$M Q%%r;c ti}g}|jD]\}}t|fi|\}}|||<||z } ||fSN)itemsr ) commandresrBcursorsret node_nameresponsecursorrs r9parse_scan_resultrN8sSG C"yy{ 8x373 #  q + C<r;c t}|jD]}|D]\}} ||xx|z cc<|jDcgc] \}}||f }}}|S#t$r|||<YNwxYwcc}}wrD)rvaluesKeyErrorrE) rFrGrBnumsub_d numsub_tupschannel numsubbednumsub ret_numsubs r9parse_pubsub_numsubrXCs}Hzz| "- GY .!Y.!#.$>;KL7F#LJL   .$-! .Ms A A2A/.A/resprBc |jdd dtdtttff fd }i}|D].}|dd\}}}|dd}|||D cgc]} ||  c} d|||f<0|Scc} w)N current_hostrAr4c2t|dxs|dfSNr)r1)rAr[s r9 fix_serverz'parse_cluster_slots..fix_serverUs DG$4 d1g==r;)primaryreplicas)getrr str) rYrBr`slotsslotstartendrbrcreplicar[s @r9parse_cluster_slotsrkPs;;~r2L>#>%S/> E"2AhsG8!7+=EF'W-F eSj LGs A*c t|dtr|Sg}|D]}ggd}tdt|ddD]'}|dj |d||d|dzf)|d}|D]A}i}tdt|dD]}||dz|||<|dj |C|j ||S)z( Parse CLUSTER SHARDS response. r)rfnodesr_rfrarm) isinstancedictrangelenappend) rYrBshardsxshardirmnode dict_nodes r9parse_cluster_shardsrzds$q'4  F r*q#ad)Q'A 'N ! !1Q47QqT!a%[": ;(!DI1c$i+%)!a%[ $q'", 'N ! !) ,   e Mr;c $|jdS)z+ Parse CLUSTER MYSHARDID response. utf-8)decode)rYrBs r9parse_cluster_myshardidr~zs ;;w r;rbrjzslot-id)$charsetconnection_classr>connection_pool_class client_namecredential_providerdbdecode_responsesencodingencoding_errorserrorsr2lib_name lib_versionmax_connections nodes_flagredis_connect_funcpasswordr3 queue_classretryretry_on_timeoutprotocolsocket_connect_timeoutsocket_keepalivesocket_keepalive_optionssocket_timeoutssl ssl_ca_certs ssl_ca_data ssl_certfile ssl_cert_reqs ssl_keyfile ssl_passwordunix_socket_pathusernamer8c x|jDcic]\}}|tvr |tvr||}}}|Scc}}w)z9 Remove unsupported or disabled keys from kwargs )rEREDIS_ALLOWED_KEYSKWARGS_DISABLED_KEYS)kwargskvconnection_kwargss r9cleanup_kwargsrsN LLN Aq " "q0D'D 1  s6c @eZdZeej eeee e e dZy) ClusterParser)ASKTRYAGAINMOVED CLUSTERDOWN CROSSSLOT MASTERDOWNN) __name__ __module__ __qualname__r-rEXCEPTION_CLASSESrr(r"rrr!r7r;r9rrs-"''%+.)   r;rceZdZdZdZdZdZdZdZeeeeehZ e e gdee gdee d gee gd e Z gd fZeeed Ze e d dgee dgde gdee gdde ddgde dgde dgee dgde dgde dgd ZeeefZd"d#d!Zy )$AbstractRedisCluster primariesrcallrandomz default-node)WzACL CATz ACL DELUSERz ACL DRYRUNz ACL GENPASSz ACL GETUSERzACL HELPzACL LISTzACL LOGzACL LOADzACL SAVEz ACL SETUSERz ACL USERSz ACL WHOAMIAUTH CLIENT LISTzCLIENT SETINFOCLIENT SETNAMECLIENT GETNAME CONFIG SETCONFIG REWRITECONFIG RESETSTATTIMEPUBSUB CHANNELS PUBSUB NUMPAT PUBSUB NUMSUBPUBSUB SHARDCHANNELSPUBSUB SHARDNUMSUBPINGINFOSHUTDOWNKEYSDBSIZEBGSAVE SLOWLOG GET SLOWLOG LEN SLOWLOG RESETWAITWAITAOFSAVE MEMORY PURGEzMEMORY MALLOC-STATSz MEMORY STATSLASTSAVEzCLIENT TRACKINGINFO CLIENT PAUSECLIENT UNPAUSECLIENT UNBLOCKz CLIENT IDz CLIENT REPLYzCLIENT GETREDIRz CLIENT INFO CLIENT KILLREADONLYz CLUSTER INFOz CLUSTER MEETCLUSTER MYSHARDIDz CLUSTER NODESzCLUSTER REPLICASz CLUSTER RESETzCLUSTER SET-CONFIG-EPOCH CLUSTER SLOTSCLUSTER SHARDSzCLUSTER COUNT-FAILURE-REPORTSzCLUSTER KEYSLOTCOMMANDz COMMAND COUNTz COMMAND LISTzCOMMAND GETKEYS CONFIG GETDEBUG RANDOMKEYr READWRITErzTFUNCTION LOADzTFUNCTION DELETEzTFUNCTION LISTTFCALL TFCALLASYNCz GRAPH.CONFIGzLATENCY HISTORYzLATENCY LATESTz LATENCY RESETz MODULE LISTz MODULE LOADz MODULE UNLOADz MODULE LOADEX) FLUSHALLFLUSHDBzFUNCTION DELETEzFUNCTION FLUSHz FUNCTION LISTz FUNCTION LOADzFUNCTION RESTOREzREDISGEARS_2.REFRESHCLUSTERSCAN SCRIPT EXISTS SCRIPT FLUSH SCRIPT LOADz FUNCTION DUMP)zCLUSTER COUNTKEYSINSLOTzCLUSTER DELSLOTSzCLUSTER DELSLOTSRANGEzCLUSTER GETKEYSINSLOTzCLUSTER SETSLOT)z FT.CREATEz FT.SEARCHz FT.AGGREGATEz FT.EXPLAINz FT.EXPLAINCLIz FT,PROFILEzFT.ALTERz FT.DROPINDEXz FT.ALIASADDzFT.ALIASUPDATEz FT.ALIASDELz FT.TAGVALSz FT.SUGADDz FT.SUGGETz FT.SUGDELz FT.SUGLENz FT.SYNUPDATEz FT.SYNDUMPz FT.SPELLCHECKz FT.DICTADDz FT.DICTDELz FT.DICTDUMPzFT.INFOzFT._LISTz FT.CONFIGzFT.ADDzFT.DELzFT.DROPzFT.GETzFT.MGETz FT.SYNADD)rrrrrrcFtt|jSrD)sumlistrPrFrGs r9zAbstractRedisCluster.|sCSZZ\8J4Kr;)rrr) rrrrrrrrrrrcXt|trt|jS|SrD)rorprrPrs r9rzAbstractRedisCluster.!jd6KSZZ\!2!TQT!Tr;rrcXt|trt|jS|SrD)rorprrPrs r9rzAbstractRedisCluster.rr;rcBt|jdkDrdSdSr^)rrPrs r9rzAbstractRedisCluster.s#cjjl:Ka:OQ5VUV5Vr;rrcPt|jjSrD)rrPpoprs r9rzAbstractRedisCluster.s$szz|2D2H2H2Jr;rcft|jDcgc] }t|c}Scc}wrD)ziprPr)rFrGrs r9rzAbstractRedisCluster.s!S#**,EW4XSV4X4Xs.rc4t|jSrD)rrPrs r9rzAbstractRedisCluster.sSZZ\ARr;Nc|r||j_y|j}|jDcgc] }||k7s | }}|r%t j ||j_y|j Dcgc] }||k7s | }}|r%t j ||j_yycc}wcc}w)aReplace the default cluster node. A random cluster node will be chosen if target_node isn't passed, and primaries will be prioritized. The default node will not be changed if there are no other nodes in the cluster. Args: target_node (ClusterNode, optional): Target node to replace the default node. Defaults to None. N) nodes_manager default_nodeget_default_node get_primariesrchoice get_replicas)self target_node curr_noderxrrcs r9replace_default_nodez)AbstractRedisCluster.replace_default_nodes .9D   +--/I*.*<*<*>T$$)BSTIT28-- 2J""/.2->->-@VTDIDUDVV6*,RSM'R*<9IJNr;rceZdZedZ d5deededeeddeded d e d ed e d e deedee e eefge eefffdZ dZ dZdZdZdZdZd6dZdZdZdZdZd7dZdZdZded fd Zd8d!Zd9d"Zd6d#Zd:d$Z d;d%Z d&Z!dedfd'Z"d(Z#d)Z$d*Z%d+Z&d,Z'd-Z(d.Z)d/Z*d0Z+d1Z,d2Z-d3Z.d4Z/y)< RedisClusterc |dd|i|S)a Return a Redis client object configured from the given URL For example:: redis://[[username]:[password]]@localhost:6379/0 rediss://[[username]:[password]]@localhost:6379/0 unix://[username@]/path/to/socket.sock?db=0[&password=password] Three URL schemes are supported: - `redis://` creates a TCP socket connection. See more at: - `rediss://` creates a SSL wrapped TCP socket connection. See more at: - ``unix://``: creates a Unix Domain Socket connection. The username, password, hostname, path and all querystring values are passed through urllib.parse.unquote in order to replace any percent-encoded values with their corresponding characters. There are several ways to specify a database number. The first value found will be used: 1. A ``db`` querystring option, e.g. redis://localhost?db=0 2. If using the redis:// or rediss:// schemes, the path argument of the url, e.g. redis://localhost/0 3. A ``db`` keyword argument to this function. If none of these options are specified, the default db=0 is used. All querystring options are cast to their appropriate Python types. Boolean arguments can be specified with string values "True"/"False" or "Yes"/"No". Values that cannot be properly cast cause a ``ValueError`` to be raised. Once parsed, the querystring arguments and keyword arguments are passed to the ``ConnectionPool``'s class initializer. In the case of conflicting arguments, querystring arguments always win. urlr7r7)clsrrs r9from_urlzRedisCluster.from_urlsT%s%f%%r;Nr2r3 startup_nodesrcluster_error_retry_attemptsrr+require_full_coveragereinitialize_stepsread_from_replicasdynamic_startup_nodesr address_remapc .|g}d| vr tdd} | d} t| }d|vr tdd|vr|ddk7r td | j|| jd }| jd |}|j t ||n9|||j t ||nt |dk(r td | jd d|_| jd |jitdi| } |r%||_ | jd|jin%| jdttdit| jdd| jdd| jdd|_||_|j"j$j'|_|j"j*j'|_||_d|_||_t5d|| || | d| |_t9|j"j:|_t9|j"j>|_ tC||_"tGjH|_%y)a Initialize a new RedisCluster client. :param startup_nodes: List of nodes from which initial bootstrapping can be done :param host: Can be used to point to a startup node :param port: Can be used to point to a startup node :param require_full_coverage: When set to False (default value): the client will not require a full coverage of the slots. However, if not all slots are covered, and at least one node has 'cluster-require-full-coverage' set to 'yes,' the server will throw a ClusterDownError for some key-based commands. See - https://redis.io/topics/cluster-tutorial#redis-cluster-configuration-parameters When set to True: all slots must be covered to construct the cluster client. If not all slots are covered, RedisClusterException will be thrown. :param read_from_replicas: Enable read from replicas in READONLY mode. You can read possibly stale data. When set to true, read commands will be assigned between the primary and its replications in a Round-Robin manner. :param dynamic_startup_nodes: Set the RedisCluster's startup nodes to all of the discovered nodes. If true (default value), the cluster's discovered nodes will be used to determine the cluster nodes-slots mapping in the next topology refresh. It will remove the initial passed startup nodes if their endpoints aren't listed in the CLUSTER SLOTS output. If you use dynamic DNS endpoints for startup nodes but CLUSTER SLOTS lists specific IP addresses, it is best to set it to false. :param cluster_error_retry_attempts: Number of times to retry before raising an error when :class:`~.TimeoutError` or :class:`~.ConnectionError` or :class:`~.ClusterDownError` are encountered :param reinitialize_steps: Specifies the number of MOVED errors that need to occur before reinitializing the whole cluster topology. If a MOVED error occurs and the cluster does not need to be reinitialized on this current error handling, only the MOVED slot will be patched with the redirected node. To reinitialize the cluster on every MOVED error, set reinitialize_steps to 1. To avoid reinitializing the cluster on moved errors, set reinitialize_steps to 0. :param address_remap: An optional callable which, when provided with an internal network address of a node, e.g. a `(host, port)` tuple, will return the address where the node is reachable. This can be used to map the addresses at which the nodes _think_ they are, to addresses at which a client may reach them, such as when they sit behind a proxy. :**kwargs: Extra arguments that will be sent into Redis instance when created (See Official redis-py doc for supported kwargs [https://github.com/andymccurdy/redis-py/blob/master/redis/client.py]) Some kwargs are not supported and will raise a RedisClusterException: - db (Redis do not support database SELECT in cluster mode) Nrz4Argument 'db' is not possible to use in cluster modeFTpathzFRedisCluster does not currently support Unix Domain Socket connectionsrz9A ``db`` querystring option can only be 0 in cluster moder2r3a5RedisCluster requires at least one node to discover the cluster. Please provide one of the followings: 1. host and port, for example: RedisCluster(host='localhost', port=6379) 2. list of startup nodes, for example: RedisCluster(startup_nodes=[ClusterNode('localhost', 6379), ClusterNode('localhost', 6378)])rrrr|rstrictr)rrrrrr7)&r#rupdaterdrsrrrruser_on_connect_func on_connectrrr+rr encoderr __class__r copy command_flagsr  node_flagsrreinitialize_counterr NodesManagerrrrcluster_response_callbacksrresult_callbacksr commands_parser threadingr*_lock)rr2r3rrrrrrrrrrr url_optionss r9__init__zRedisCluster.__init__sX  M 6>'F   ?H#C.K$+){"{4'8A'=+O MM+ &::f%D::fd+D  T4!8 9  $"2  T4!8 9  1 $'4 %+JJ/CT$J! +T__=>)&) DJ MM7DJJ/ 0 MM7E/*;Q$?@ A JJz7 + JJ(( 3 JJ)5 1  -I)!^^99>>@..3388:"4$%!"4) '"7"7'    +> NN > >+ '!4DNN4S4S T-d3^^% r;c|SrDr7rs r9 __enter__zRedisCluster.__enter__s r;c$|jyrDcloserexc_type exc_value tracebacks r9__exit__zRedisCluster.__exit__  r;c$|jyrDr6r3s r9__del__zRedisCluster.__del__r=r;c|jD]4}|js |jjj6y#t$rYCwxYwrD) get_nodesredis_connectionr> disconnectOSErrorrrxs r9disconnect_connection_poolsz(RedisCluster.disconnect_connection_poolssMNN$D$$))99DDF%s$A  AAc|jt|j|jr8|j dt |j dk7r td|j|j|yy)z Initialize the connection, authenticate and select a database and send READONLY if it is set during object initialization. rOKzREADONLY command failedN) set_parserrr#r send_commandr1 read_responserr")rr=s r9r#zRedisCluster.on_connectsx m,  " "  # #J /J44674?%&?@@  $ $ 0  % %j 1 1r;c|jsI|j5|js|jj|gddd|jS|jS#1swY|jSxYwrD)rBr/rcreate_redis_connectionsrEs r9get_redis_connectionz!RedisCluster.get_redis_connectionsk$$ H,,&&??G H$$$t$$$ H$$$s )A""A6c<|jj|||SrD)rget_noderr2r3rJs r9rPzRedisCluster.get_nodes!!**4yAAr;c@|jjtSrD)rget_nodes_by_server_typePRIMARYr3s r9rzRedisCluster.get_primaries!!::7CCr;c@|jjtSrD)rrSREPLICAr3s r9rzRedisCluster.get_replicasrUr;ctjt|jjj SrD)rrrr nodes_cacherPr3s r9get_random_nodezRedisCluster.get_random_nodes+}}T$"4"4"@"@"G"G"IJKKr;c\t|jjjSrD)rrrYrPr3s r9rAzRedisCluster.get_nodess"D&&2299;<a 47)4::<@R@RRa 47),224GZZ d3  !%L --11':L 4>>00 0((*+ + T^^55 5%%' ' T^^44 4$$& & T^^55 5>># # T^^88 8&&334 4 66q9 9&&334 4'4&&-D%%88d--J']2JD6Mr;cZ|jdk(ry|j|jzdk(S)NrF)rr)r3s r9_should_reinitializedz"RedisCluster._should_reinitializeds1  " "a ',,t/F/FF!K Kr;cN|jj|}t|S)z Calculate keyslot for a given key. See Keys distribution model in https://redis.io/topics/cluster-spec )r$encoder)rr`rs r9r^zRedisCluster.keyslots" LL   ${r;cp|jj}|jj|g|S)a Get the keys in the command. If the command has no keys in in, None is returned. NOTE: Due to a bug in redis<7.0, this function does not work properly for EVAL or EVALSHA when the `numkeys` arg is 0. - issue: https://github.com/redis/redis/issues/9493 - fix: https://github.com/redis/redis/pull/9733 So, don't use this function with EVAL or EVALSHA. )rrBr-get_keys)rrA redis_conns r9_get_command_keyszRedisCluster._get_command_keyss6**,== ,t##,,Z?$??r;c|d}|jj|tk(r|dS|jdvr]t |dkrt d|t |d}|dd|z}t |dk(rtjdtS|}nY|j|}|t |dk(r:|jdvrtjdtSt d|t |dk(r|j|dS|Dchc]}|j|}}t |dk7rt |d |jScc}w) a Figure out what slot to use based on args. Raises a RedisClusterException if there's a missing key and we can't determine what slots to map the command to; or, if the keys don't all map to the same key slot. rr_)EVALEVALSHArnzInvalid args in command: ra)FCALLFCALL_ROzNo way to dispatch this command to Redis Cluster. Missing key. You can execute the command by specifying target nodes. Command: z) - all keys must map to the same key slot) r'rdr rrrr#intr randrangerrr^r)rrArFnum_actual_keys eval_keyskeysr`rfs r9rzRedisCluster.determine_slotsyq'    ! !' *g 57N ==?1 14yA~+.Gv,NOO!$q'lOQ_!45I9~"''+CDDD)4))40D|s4yA~==?&;;!++A/GHH+//3f6 t9><<Q( (/33sc"33 u:?')DE yy{ 4sE!c|jS)z. Get the connections' encoder )r$r3s r9 get_encoderzRedisCluster.get_encoders||r;c.|jjS)z9 Get the connections' key-word arguments )rrr3s r9get_connection_kwargsz"RedisCluster.get_connection_kwargss!!333r;cBt|txr||jvSrD)rorer()r target_nodess r9_is_nodes_flagzRedisCluster._is_nodes_flags,,P1PPr;ct|tr|}|St|tr|g}|St|tr|j }|St dt |)Nztarget_nodes type can be one of the following: node_flag (PRIMARIES, REPLICAS, RANDOM, ALL_NODES),ClusterNode, list, or dict. The passed type is )rorrrprP TypeErrortype)rrrms r9_parse_target_nodesz RedisCluster._parse_target_nodess{ lD ) E   k 2!NE  d +!'')E &'+<&8%9; r;cd}d}d}|jdd}|$|j|s|j|}d}|rdn |j}d|z}t |D]} i} |sM|j |i|d|i}|st d|d t|dk(r|d|jk(rd}|D]%} |j| g|i|| | j<'|j|d| fi|cSy#t$rI} |dkDr=t| |jjvr|r|j!|dz}Yd} ~ | d} ~ wwxYw) a" Wrapper for ERRORS_ALLOW_RETRY error handling. It will try the number of times specified by the config option "self.cluster_error_retry_attempts" which defaults to 3 unless manually configured. If it reaches the number of times, the command will raise the exception Key argument :target_nodes: can be passed with the following types: nodes_flag: PRIMARIES, REPLICAS, ALL_NODES, RANDOM ClusterNode list dict FNrTrr_r!No targets were found to execute command on)rrrrrqrr#rrr_execute_commandrg_process_result Exceptionrr%rr) rrArtarget_nodes_specifiedis_default_noderpassed_targetsretry_attemptsexecute_attempts_rGrxes r9execute_commandzRedisCluster.execute_commands "' ND9  %d.A.A..Q33NCL%) "(AT-N-N ~-'(A -#84#8#8$!'$4B$L(3?v[QL)Q.(Ot/D/D/FF*.(D%:T%:%:4%Q$%Q&%QC N),t++DGSCFCC))* !A%$q'T^^5V5V*V&113#a'NG s#BC99 E =EEE c|d}d}d}d}d}d} t|j} | dkDr| dz} |r|j|}nC| rA|j|} |jj | |j xr|tv}d} |j|}t|g|i|}|r'|jd|j|dfi|d}|j||j||fi|} ||jvr|j|| fi|} | ||jj|SStMd #t$rt t"f$rj} ||j%|jj&j)|j*dd|_|jj/| d} ~ wt0$rn} |xj2dz c_|j5r"|jj/d|_n|jj7| d} Yd} ~ nd} ~ wt8$r*| |jdz krt;j<d Ynt>$r-} tA| jB| jD }d}Yd} ~ nnd} ~ wtF$r6} t;j<d |jj/| d} ~ wtH$rtJ$r} |r|j%| d} ~ wwxYw|?|jj|n##||jj|wwxYw| dkDr&) z9 Send a command to a node in the cluster rNFr_rfASKINGTrng?r8g?zTTL exhausted.)'rrrPrrrrrrNr?rJparse_responser+r>releaserrr'rCrrrgrB initializer"r)rupdate_moved_exceptionr(timerzrr:r2r3rr%rr)rrrArrFr@r= redirect_addraskingmovedttlrgrKrs r9rzRedisCluster._execute_command]sGq'   $--.Ag 1HCR C"&----"HK/4..5D"&"4"4"G"Gd55R']:R#K"E!66{C +JHHH ++H5-J--j(MfM"F' ''.4:44ZSFSd===Gt>>wG  $* H p)..66zB*+,,u' #\2  )))+""0044[5E5EtL/3 ,""--/ ))Q.)--/&&11301D-&&==a@  %44q88JJt$  -166 G #  4 ""--/   ))+  )..66zB)..66zB*gAgsnC)ELA%G L A$H61L'63L)L'+L3#JL' L'1KL-LLL'' Mc |j5|jr|jjdddy#1swYyxYw#t$rYywxYwrD)r/rr7AttributeErrorr3s r9r7zRedisCluster.closesQ  /%%&&,,. / / /   s+ A '>A AA A AAc ||jvr|j|||fi|St|dk(rt|jdS|S)a  Process the result of the executed command. The function would return a dict or a single value. :type command: str :type res: dict `res` should be in the following format: Dict r_r)r,rrrrP)rrFrGrs r9rzRedisCluster._process_resultsY d++ +14((1'3I&I I X] %a( (Jr;ct|||y)a This function can be used to add externally defined redis modules, and their namespaces to the redis client. ``funcname`` - A string containing the name of the function to create ``func`` - The function, being added to this class. N)setattr)rfuncnamefuncs r9load_external_modulez!RedisCluster.load_external_modules h%r;) NiNraNFFTNNNNN)F)rr+r4NrDNN)Ng?TNNT)0rrr classmethodrrrerrboolrr r1r4r<r?rFr#rNrPrrrZrArcrrhrjrlrnrqrwrsrrrr^rrrrrrrrr7rrr7r;r9rrs)&)&Z#7;,-#'&+"##(&*!PT\&sm\&\& ] 34 \& '* \&  \& $\& \&!\& $\&c]\& %S/):E#s(O)K LM\&|2*%BDDL=$(/ 8G,3 6"N <H T<%4 3F%NL @7r 4 Q(DLb-H(&r;rc&eZdZddZdZdZdZy)rNc|dk(rtj|}||_||_t |||_||_||_y)N localhost)socket gethostbynamer2r3r:rg server_typerB)rr2r3rrBs r9r1zClusterNode.__init__sF ; ''-D  !$- & 0r;c d|jd|jd|jd|jd|jd S)Nz[host=z,port=z,name=z ,server_type=z,redis_connection=])r2r3rgrrBr3s r9__repr__zClusterNode.__repr__sWTYYK II;II;++,- $ 5 56a  9 r;cXt|txr|j|jk(SrD)rorrg)robjs r9__eq__zClusterNode.__eq__s!#{+EDII0EEr;cR|j|jjyyrD)rBr7r3s r9r?zClusterNode.__del__s%  ,  ! ! ' ' ) -r;r)rrrr1rrr?r7r;r9rrs1 F*r;rc>eZdZdZd deddfdZdededefdZd d Zy) LoadBalancerz$ Round-Robin Load Balancing start_indexr4Nc i|_||_yrD)primary_to_idxr)rrs r9r1zLoadBalancer.__init__ s &r;rb list_sizec||jj||j}|dz|z|j|<|S)Nr_)r setdefaultr)rrbr server_indexs r9get_server_indexzLoadBalancer.get_server_indexs@**55gt?O?OP (4q(8I'EG$r;c8|jjyrD)rclearr3s r9resetzLoadBalancer.resets !!#r;)r)r4N) rrr__doc__rr1rerrr7r;r9rrs9'C'' $r;rceZdZddddedfdeeeeefgeeefffdZ ddZ dZ dZ dd Z d Zd Zd Zd ZdZdZdZdZdZdededeeeffdZy)r*FNTrc 8i|_i|_i|_d|_|j |||_||_||_||_||_ d|_ ||_ t|_ |tj}||_|j#yrD)rYr_rrpopulate_startup_nodesr_require_full_coverage_dynamic_startup_nodesrr_moved_exceptionrrread_load_balancerr.r*r/r) rrrrrsrrrrs r9r1zNodesManager.__init__s  ##M2  &;#&;#%:"* $!'".. <>>#D  r;c|rB|r@|dk(rtj|}|jjt ||S|r|jj|Sy)z Get the requested node from the cluster's nodes. nodes. :return: ClusterNode if the node exists, else None rr8N)rrrYrdr:rQs r9rPzNodesManager.get_node6s\ D{"++D1##'' 4d(KL L ##'' 2 2r;c||_yrD)r)r exceptions r9rz#NodesManager.update_moved_exceptionFs )r;c|j}|j|j|j}||jt urJt |_n>t |j|jt }||j|j<||j|jvr|j|jd}t|_|j|jj||j|jj|||j|jd<|j|k(r)||_d|_y|g|j|j<d|_y)z@ Update the slot's node with the redirected one r8Nr)rrPr2r3rrTrrYrgr_slot_idrWrsremover)rrredirected_node old_primarys r9_update_moved_slotsz NodesManager._update_moved_slotsIsE  ! !--QVV!&&-A  &**'9.5+*!&&!&&'BO5DD  _11 2 d..qyy9 9**1995a8K'.K #   QYY ' . .{ ;   QYY ' . . ?-(>(>))6:6L6L)%r;c |jrD|jd|i|jd|it|jdi|}|Std||d|}|S)Nr2r3)r>r8r7)rr!rr)rr2r3rrMs r9rzNodesManager.create_redis_nodesf == MM64. ) MM64. )&@d&@&@&J6&JKA54d5f5Ar;ct||}|j|}|L|jj|}| |j t |||}|j |k7r||_|SrD)r:rdrYrBrr)rr2r3roletmp_nodes_cacherJrs r9_get_or_create_cluster_nodez(NodesManager._get_or_create_cluster_nodesw!$- %)))4  **..y9K"k&B&B&J)$d; &&$.*. 'r;c |ji}i}g}d}d}|j}d}|jjD]} |jr |j} nF|j |j |jfi|} | |j|j_ t| jd} d}t| dk(rDt| ddddk(r-t|jdk(r|j | ddd<| D]} | d} t| d}|d k(r |j }t| d}|j!||\}}|j#||t$|}|||j<t't| dt| ddzD]*}||vrg||<||j)|t'd t| Dcgc]}| | }}|D]e}t|d}|d}|j!||\}}|j#||t*|}||j)||||j<g||d}|j|jk7s|j)|jd |jd |t|d kDstddj-||j/|}|sn|stdt1|||j3t5|j|s+|j6rtdt|dt8d||_||_|j?t$d|_ |jBr||_d|_"y#t$r tdwxYw#t$r } | }Yd} ~ d} ~ wwxYwcc}w)z Initializes the nodes cache, slots cache and redis connections. :startup_nodes: Responsible for discovering other nodes in the cluster FNrz(Cluster mode is not enabled on this nodeTr_rrnr\raz vs z on slot: rz6startup_nodes could not agree on a valid slots cache: z, zORedis Cluster cannot be connected. Please provide at least one reachable node: z9All slots are not covered after query all startup_nodes. z of z covered...)#rrrrPrBrr2r3rgr1rr%r#rrrrremap_host_portrrTrqrsrWjoinrrerMrrrrYr_rSrrr)rr tmp_slots disagreementsstartup_nodes_reachable fully_coveredrr startup_noderM cluster_slotsrrg primary_noder2r3rrwj replica_nodes replica_nodetarget_replica_nodetmp_slots r9rzNodesManager.initializesG   "' ''  ..557L 00$55A/..$))<+<+<@FANOD&&|'8'89J$01B1B?1S$TM +/'M"a' a(+A./14**+q0)5):): a #A&%#Aw #LO42:',,D<?+!11$= d">>$ 5@ 0 01s47|Sa\A-=>A )') ! !! ++K8:?3t9:M(NQa(N (N,9L#/ Q#@D#/?D)-)=)=dD)IJD$262R2R $dG_3/&aL//0CD!4, 3 8 8-: $-Q<?#==K,<,<<)00#+==/k6F6F5GzRSQT U #=1A5&;'448IIm4L3M%O'"!";?&`!55i@Mu8x''''*9~&68  %%d?+A+A+C&DE!"$'?&@A +$ 99'B1E  & &!0D  $W%/B     L)Os7AO(N/O) O /OO OOOcd|_|jjD])}|js|jj +yrD)rrYrPrBr7rEs r9r7zNodesManager.close[s> $$++-D$$%%++-.r;cX |jjy#t$rYywxYwrD)rrrr3s r9rzNodesManager.resetas+   # # ) ) +   s  ))r2r3r4cH|jr|j||fS||fS)z Remap the host and port returned from the cluster to a different internal value. Useful if the client is not connecting directly to the cluster. )r)rr2r3s r9rzNodesManager.remap_host_porths,   %%tTl3 3Tzr;r)FN)rrrrrrr rerr1rPrrrrSrrrMrrrr7rrr7r;r9r*r*s# ",PT %S/):E#s(O)K LM: *%%N!0F  +"F%P. CsuS#Xr;r*czeZdZdZ dfd ZddZdZdZdZdZ dZ d Z dd Z d Z d Zd ZdZxZS)rpz Wrapper for PubSub class. IMPORTANT: before using ClusterPubSub, read about the known limitations with pubsub in Cluster mode and learn how to workaround them: https://redis-py-cluster.readthedocs.io/en/stable/pubsub.html c "d|_|j|||||jdn$|j|jj}||_i|_|j |_t| d||j|d|y)a* When a pubsub instance is created without specifying a node, a single node will be transparently chosen for the pubsub connection on the first command execution. The node will be determined by: 1. Hashing the channel name in the request to find its keyslot 2. Selecting a node that handles the keyslot: If read_from_replicas is set to true, a replica can be selected. :type redis_cluster: RedisCluster :type node: ClusterNode :type host: str :type port: int N)r>r$push_handler_funcr7) rxset_pubsub_noderNr>clusternode_pubsub_mapping_pubsubs_generatorsuperr1r$) r redis_clusterrxr2r3rrr>r%s r9r1zClusterPubSub.__init__|s,  ]D$=yy  33DII>NN  % #% "&"9"9";  +!))/   r;c|2|j|||j|j|}||_y|3|1|j||}|j|||||}||_yt ||gdur t dd}||_y)aN The pubsub node will be set according to the passed node, host and port When none of the node, host, or port are specified - the node is set to None and will be determined by the keyslot of the channel in the first command to be executed. RedisClusterException will be thrown if the passed node does not exist in the cluster. If host is passed without port, or vice versa, a DataError will be thrown. :type cluster: RedisCluster :type node: ClusterNode :type host: str :type port: int Nr8Tz6Passing a host requires passing a port, and vice versa)_raise_on_invalid_noder2r3rPanyr rx)rrrxr2r3 pubsub_nodes r9rzClusterPubSub.set_pubsub_nodes    ' 'tyy$)) LK   $"2##D#9D  ' 'tT BK  $ $ &TU UK r;c|jS)zJ Get the node that is being used as the pubsub connection )rxr3s r9get_pubsub_nodezClusterPubSub.get_pubsub_nodesyyr;cd||j|jtd|d|dy)zl Raise a RedisClusterException if the node is None or doesn't exist in the cluster. NrfzNode r6z doesn't exist in the cluster)rPrgr#)rr!rxr2r3s r9r#z$ClusterPubSub._raise_on_invalid_nodesF <=11DII1FN'vQtf$AB Or;c|jS|jt|dkDr[|d}|jj |}|jj j ||jj}n|jj}||_ |jj|}|j|_|jjd|j|_|jj|j|j5t s/|jj"j%|j|j}|j&||j(g|y)z Execute a subscribe/unsubscribe command. Taken code from redis-py and tweak to make it work within a cluster. Nr_rq)r=r>rrrr^rrrrZrxrNr?rvregister_connect_callbackr#rr,_parserset_push_handler_executerJ)rrArTrgrxrBr=s r9rzClusterPubSub.execute_commands5 ?? "##+t9q=#1gG<<//8D<<55HHdll==D  <<779D  #'<<#D#DT#J '7'G'G$"22AA$//DO OO 5 5doo F%%1:K''889O9OP__  j*"9"9ADAr;c |j|jS#t$rD|jj |j }||j|j<|cYSwxYw)N)r)rrgrQrBrqr)rrxrqs r9_get_node_pubsubzClusterPubSub._get_node_pubsubsj ++DII6 6 **11"&"8"82F39D $ $TYY /M  sA A('A(ctt|jD]-}t|j}|j }|+|cSyrD)rqrrrnextr get_message)rrrqmessages r9_sharded_message_generatorz(ClusterPubSub._sharded_message_generatorsJs43345A$112F((*G" 6 r;c#TK |jjD]}|$wrD)rrPrrqs r9rz ClusterPubSub._pubsubs_generator s,2299; <s&(c|r+|j|jj||}n|j}|yt |ddk(r|d|j vr|j j |d|jj|dd|jj|d}|j|jjdur%|jj|j|js2|js&|js|jj|j s|ry|S)N)ignore_subscribe_messagesryr sunsubscriberTF)rrgr2r4r1"pending_unsubscribe_shard_channelsrshard_channelsrrrc subscribedchannelspatternssubscribed_eventrr8)rr8ryrr3rxs r9get_sharded_messagez!ClusterPubSub.get_sharded_messages. ..{/?/?@LL*CWMG557G ? '&/ *n <y!T%L%LL77>>wy?QR##'' (:DA||55gi6HI++DII6AAUJ,,00;}}T]]4;N;N  ! ! ' ' )  ) )-Fr;ch|rt|d|dd}tj|}|j||j D]\}}|j j |}|j|}|r|jdi||in|j||jj|j|jj|j|di|js|jr|jjd|_y)Nrr_r7)rrpfromkeysr!rErrcr/ ssubscriber;r:difference_update_normalize_keysr<r?sethealth_check_response_counter)rrAr s_channels s_channelhandlerrxrqs r9rCzClusterPubSub.ssubscribe*s Qab2D]]4( &!","2"2"4 Iw<<11)s} Qab2D&&DI<<11)>@*D',H)$%!"4 JJz7 + JJ(( 3 JJ)5 1  <>>#D r;c.t|jSr)rrr3s r9rzClusterPipeline.__repr__st*%%&'r;c|SrVr7r3s r9r4zClusterPipeline.__enter__s r;c$|jyrN)rr8s r9r<zClusterPipeline.__exit__s  r;cD |jy#t$rYywxYwrD)rrr3s r9r?zClusterPipeline.__del__s"  JJL   s  c,t|jSrV)rrrSr3s r9__len__zClusterPipeline.__len__s4%%&&r;cy)z?Pipeline instances should always evaluate to True on Python 3+Tr7r3s r9__bool__zClusterPipeline.__bool__sr;c&|j|i|S)z? Wrapper function for pipeline_execute_command )pipeline_execute_commandrrArs r9rzClusterPipeline.execute_commands-t,,d=f==r;c x|jjt||t|j|S)zN Appends the executed command to the pipeline's command stack )rSrsPipelineCommandrrrrArBs r9r`z(ClusterPipeline.pipeline_execute_commands6 !! D'3t/A/A+B C  r;c|D]J}|j}t|ts |j||jdz|j |y)z8 Raise the first exception on the stack r_N)resultrorannotate_exceptionpositionrA)rstackcrMs r9raise_first_errorz!ClusterPipeline.raise_first_errorsDAA!Y'''1::>166B r;cdjtt|}d|d|d|jd}|f|jddz|_y)zS Provides extra context to the exception prior to it being handled rz Command # z (z) of pipeline caused error: rr_N)r mapr0rA)rrnumberrFcmdmsgs r9rgz"ClusterPipeline.annotate_exceptions_hhs8W-.3%(&^^A./ 1 ).."44 r;c|j} |j|||jS#|jwxYw)zB Execute all the commands in the current pipeline )rSsend_cluster_commandsr)rraise_on_errorris r9executezClusterPipeline.executes7"" --e^D JJLDJJLs 0AcLg|_t|_d|_d|_y)z/ Reset back to empty pipeline. FN)rSrFscriptswatchingexplicit_transactionr3s r9rzClusterPipeline.resets& u  $)!r;c|sgS|j} |j|||S#ttf$r}|dkDr|dz}n|Yd}~nd}~wwxYw?)a Wrapper for CLUSTERDOWN error handling. If the cluster reports it is down it is assumed that: - connection_pool was disconnected - connection_pool was reseted - refereh_table_asap set to True It will try the number of times specified by the config option "self.cluster_error_retry_attempts" which defaults to 3 unless manually configured. If it reaches the number of times, the command will raises ClusterDownException. )rsallow_redirectionsrr_N)r_send_cluster_commandsrr)rrirsrzrrs r9rrz%ClusterPipeline.send_cluster_commandss}$I:: 22#1'93 %o6 !A%#a'NG  s'A  AA ct|d}d}i}|D],} |jjdd}|r#|j|s|j |} n7|j |j d|i} | std|j d t| d kDrtd |j | d } | |jk(rd}| j} | |vrL|j| } t| |j } t+| j,| j| || <|| j/|/|j} |j}|D]}|j1|D]}|j3 |jD]'}|jj!|j") td |Dd}|r|r|xj4d z c_|j7r,|j$j'|r|j)|D]+} t9|t|j i|j|_-g}t|dD]t}|j d |j@vr<|j@|j d |j<fi|j|_|j/|j<v|r|jC||S#t$rh|jD]'}|jj!|j")|j$j'|r|j)wxYw#|jD]'}|jj!|j")wxYw#t>$r}||_Yd}~d}~wwxYw)z Send a bunch of cluster commands to the redis cluster. `allow_redirections` If the pipeline should follow `ASK` & `MOVED` responses automatically. If set to false it will raise RedisClusterException. c|jSrDrhrus r9rz8ClusterPipeline._send_cluster_commands..+sajjr;)r`FTrN node_flagrrr_zToo many targets for command rc3hK|]*}t|jtjr|,ywrD)rorfrtr).0rjs r9 z9ClusterPipeline._send_cluster_commands..s, ahh(J(JK s02c|jSrDr~rs r9rz8ClusterPipeline._send_cluster_commands..s!**r;c|jSrDr~rs r9rz8ClusterPipeline._send_cluster_commands..sQZZr;)"sortedrBrrrrrAr#rrrrgrNr?rrPr>rr=rrr NodeCommandsrrswritereadr)rr rrfr$r+rk)rrirsrzattemptrrmrjrrrxrJr@r=r node_commandsrrKr%s r9r{z&ClusterPipeline._send_cluster_commandss$89 A "#~t!D!$*=*=n*M#'#;#;N#KL#84#8#8$+9$L(3?x{S|$q(/7x@$A40022&*O !II E)!%!:!:4!@J %3J%G (4"11"22"(E)$ i ''*cv   8!LLNM" ###*\\^!!))!,,7$    %   )&  % % * %))+""--/"--/! %w6L!))LAH #78AvvayD;;;E4::166!9EHH !  OOAHH % 9   " "5 )e+!&A--55allC"0**557* 557h\\^!!))!,,7$\"! AHH!s15L>M;(N:A1M8;unlinking multiple keys is not implemented in pipeline commandUNLINKrrrs r9unlinkzClusterPipeline.unlink s8 u:?'P ##HeAh77r;)NNNFrarN)T)TT)-rrrrrr'r"rr(rrrrerrrrr1rr4r<r?r\r^rr`rkrgrtrrrr{rrrrrrrrrrrrrOrPs@r9rtrt]sJ  ;?DH7;#(,-"# #%#*##4X #67 # %-T#x--@$A #  ] 34 #!#'*# #J( '>  5*B>B#L>BjX5HI XPIK 5 8r;rtrg.cfd}|S)zi Prints error because some pipelined commands should be blocked when running in cluster-mode c"tdd)Nz"ERROR: Calling pipelined function z1 is blocked when running redis in cluster mode...r)rArrgs r9innerz%block_pipeline_command..inner s!#074 5  r;r7)rgrs` r9block_pipeline_commandr s  Lr;)E BGREWRITEAOFrBITOP BRPOPLPUSHrrrrCLIENTrrrrCONFIGrECHOrrrrrrMGETzMGET NONATOMICMOVEMSETzMSET NONATOMICMSETNXPFCOUNTPFMERGErPUBLISHrrrRENAMERENAMENX RPOPLPUSHrrrrz SCRIPT KILLrSCRIPTSDIFF SDIFFSTOREz SENTINEL GET MASTER ADDR BY NAMEzSENTINEL MASTERzSENTINEL MASTERSzSENTINEL MONITORzSENTINEL REMOVEzSENTINEL SENTINELSz SENTINEL SETzSENTINEL SLAVESSENTINELrSINTER SINTERSTORESLAVEOFrrrSLOWLOGSMOVESORTSUNION SUNIONSTORErrrceZdZdZddZy)rcrNc`||_|i}||_||_d|_d|_d|_y)NF)rArBrhrfrxr)rrArBrhs r9r1zPipelineCommand.__init__n s6 ?G      r;r)rrrrr1r7r;r9rcrck s  r;rcc(eZdZdZdZdZdZdZy)rrc<||_||_||_g|_yrY)rr>r=commands)rrr>r=s r9r1zNodeCommands.__init__| s ,.$ r;c:|jj|yrY)rrs)rrjs r9rszNodeCommands.append s Qr;c |j}|j}|D] }d|_  |j|j |Dcgc]}|j c}ycc}w#t tf$r}|D] }||_ Yd}~yd}~wwxYw)z= Code borrowed from Redis so it can be fixed N)r=rrfsend_packed_command pack_commandsrArr')rr=rrjrs r9rzNodeCommands.write s__ ==AAH    * *((()CQ!&&)CD )C.  s)A&A!  A&!A&&B 5BB cp|j}|jD]@}|j |j||jdfi|j |_By#t tf$r#}|jD] }||_ Yd}~yd}~wt$rtjd|_YwxYw)rNrr_) r=rrfrrArBrr'r$sysexc_info)rr=rjrs r9rzNodeCommands.read s__ A&xx12t22:qvvayVAIIVAH+,(6!]]#$+!1"||~a0AH1s0AB5,B  (B54B5N)rrrrr1rsrrr7r;r9rry s  ,1r;r)`rrrr.r collectionsrtypingrrrrrr r redis._parsersr r redis._parsers.helpersr redis.backoffr redis.clientrrrredis.commandsrrredis.commands.helpersrredis.connectionrrr redis.crcrrredis.exceptionsrrrrrrr r!r"r#r$r%r&r'r( redis.lockr* redis.retryr+ redis.utilsr,r-r.r/r0r1rerr:r?rNrXrkrzr~rTrWr rrrrrrrrr*rprtrPIPELINE_BLOCKED_COMMANDSrFreplacelowerrrcrr7r;r9rs  #DDD2-);;>/EE8"5c?s    %S/4S> )*(,    %L(  M qNqNhi &')=i &X**8$$&XXv g+Fg+Tn8ln8b  #s();  FN)Gooc3'--/G OW&