redis 5.3.0b4__py3-none-any.whl → 6.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redis/__init__.py +2 -11
- redis/_parsers/base.py +14 -2
- redis/_parsers/resp3.py +2 -2
- redis/asyncio/client.py +103 -83
- redis/asyncio/cluster.py +147 -102
- redis/asyncio/connection.py +77 -24
- redis/asyncio/lock.py +26 -5
- redis/asyncio/retry.py +12 -0
- redis/asyncio/sentinel.py +11 -1
- redis/asyncio/utils.py +1 -1
- redis/auth/token.py +6 -2
- redis/backoff.py +15 -0
- redis/client.py +160 -138
- redis/cluster.py +211 -82
- redis/commands/cluster.py +1 -11
- redis/commands/core.py +219 -207
- redis/commands/helpers.py +19 -76
- redis/commands/json/__init__.py +1 -1
- redis/commands/redismodules.py +5 -17
- redis/commands/search/aggregation.py +3 -1
- redis/commands/search/commands.py +43 -16
- redis/commands/search/dialect.py +3 -0
- redis/commands/search/profile_information.py +14 -0
- redis/commands/search/query.py +5 -1
- redis/commands/timeseries/__init__.py +1 -1
- redis/commands/vectorset/__init__.py +46 -0
- redis/commands/vectorset/commands.py +367 -0
- redis/commands/vectorset/utils.py +94 -0
- redis/connection.py +89 -33
- redis/exceptions.py +4 -1
- redis/lock.py +24 -4
- redis/ocsp.py +2 -1
- redis/retry.py +12 -0
- redis/sentinel.py +3 -1
- redis/typing.py +1 -1
- redis/utils.py +114 -1
- {redis-5.3.0b4.dist-info → redis-6.0.0.dist-info}/METADATA +57 -23
- redis-6.0.0.dist-info/RECORD +78 -0
- {redis-5.3.0b4.dist-info → redis-6.0.0.dist-info}/WHEEL +1 -2
- redis/commands/graph/__init__.py +0 -263
- redis/commands/graph/commands.py +0 -313
- redis/commands/graph/edge.py +0 -91
- redis/commands/graph/exceptions.py +0 -3
- redis/commands/graph/execution_plan.py +0 -211
- redis/commands/graph/node.py +0 -88
- redis/commands/graph/path.py +0 -78
- redis/commands/graph/query_result.py +0 -588
- redis-5.3.0b4.dist-info/RECORD +0 -82
- redis-5.3.0b4.dist-info/top_level.txt +0 -1
- /redis/commands/search/{indexDefinition.py → index_definition.py} +0 -0
- {redis-5.3.0b4.dist-info → redis-6.0.0.dist-info/licenses}/LICENSE +0 -0
redis/client.py
CHANGED
|
@@ -2,9 +2,19 @@ import copy
|
|
|
2
2
|
import re
|
|
3
3
|
import threading
|
|
4
4
|
import time
|
|
5
|
-
import warnings
|
|
6
5
|
from itertools import chain
|
|
7
|
-
from typing import
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
Any,
|
|
9
|
+
Callable,
|
|
10
|
+
Dict,
|
|
11
|
+
List,
|
|
12
|
+
Mapping,
|
|
13
|
+
Optional,
|
|
14
|
+
Set,
|
|
15
|
+
Type,
|
|
16
|
+
Union,
|
|
17
|
+
)
|
|
8
18
|
|
|
9
19
|
from redis._parsers.encoders import Encoder
|
|
10
20
|
from redis._parsers.helpers import (
|
|
@@ -13,6 +23,7 @@ from redis._parsers.helpers import (
|
|
|
13
23
|
_RedisCallbacksRESP3,
|
|
14
24
|
bool_ok,
|
|
15
25
|
)
|
|
26
|
+
from redis.backoff import ExponentialWithJitterBackoff
|
|
16
27
|
from redis.cache import CacheConfig, CacheInterface
|
|
17
28
|
from redis.commands import (
|
|
18
29
|
CoreCommands,
|
|
@@ -20,6 +31,7 @@ from redis.commands import (
|
|
|
20
31
|
SentinelCommands,
|
|
21
32
|
list_or_args,
|
|
22
33
|
)
|
|
34
|
+
from redis.commands.core import Script
|
|
23
35
|
from redis.connection import (
|
|
24
36
|
AbstractConnection,
|
|
25
37
|
ConnectionPool,
|
|
@@ -40,7 +52,6 @@ from redis.exceptions import (
|
|
|
40
52
|
PubSubError,
|
|
41
53
|
RedisError,
|
|
42
54
|
ResponseError,
|
|
43
|
-
TimeoutError,
|
|
44
55
|
WatchError,
|
|
45
56
|
)
|
|
46
57
|
from redis.lock import Lock
|
|
@@ -48,11 +59,18 @@ from redis.retry import Retry
|
|
|
48
59
|
from redis.utils import (
|
|
49
60
|
HIREDIS_AVAILABLE,
|
|
50
61
|
_set_info_logger,
|
|
62
|
+
deprecated_args,
|
|
51
63
|
get_lib_version,
|
|
52
64
|
safe_str,
|
|
53
65
|
str_if_bytes,
|
|
66
|
+
truncate_text,
|
|
54
67
|
)
|
|
55
68
|
|
|
69
|
+
if TYPE_CHECKING:
|
|
70
|
+
import ssl
|
|
71
|
+
|
|
72
|
+
import OpenSSL
|
|
73
|
+
|
|
56
74
|
SYM_EMPTY = b""
|
|
57
75
|
EMPTY_RESPONSE = "EMPTY_RESPONSE"
|
|
58
76
|
|
|
@@ -173,49 +191,54 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
173
191
|
client.auto_close_connection_pool = True
|
|
174
192
|
return client
|
|
175
193
|
|
|
194
|
+
@deprecated_args(
|
|
195
|
+
args_to_warn=["retry_on_timeout"],
|
|
196
|
+
reason="TimeoutError is included by default.",
|
|
197
|
+
version="6.0.0",
|
|
198
|
+
)
|
|
176
199
|
def __init__(
|
|
177
200
|
self,
|
|
178
|
-
host="localhost",
|
|
179
|
-
port=6379,
|
|
180
|
-
db=0,
|
|
181
|
-
password=None,
|
|
182
|
-
socket_timeout=None,
|
|
183
|
-
socket_connect_timeout=None,
|
|
184
|
-
socket_keepalive=None,
|
|
185
|
-
socket_keepalive_options=None,
|
|
186
|
-
connection_pool=None,
|
|
187
|
-
unix_socket_path=None,
|
|
188
|
-
encoding="utf-8",
|
|
189
|
-
encoding_errors="strict",
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
redis_connect_func=None,
|
|
201
|
+
host: str = "localhost",
|
|
202
|
+
port: int = 6379,
|
|
203
|
+
db: int = 0,
|
|
204
|
+
password: Optional[str] = None,
|
|
205
|
+
socket_timeout: Optional[float] = None,
|
|
206
|
+
socket_connect_timeout: Optional[float] = None,
|
|
207
|
+
socket_keepalive: Optional[bool] = None,
|
|
208
|
+
socket_keepalive_options: Optional[Mapping[int, Union[int, bytes]]] = None,
|
|
209
|
+
connection_pool: Optional[ConnectionPool] = None,
|
|
210
|
+
unix_socket_path: Optional[str] = None,
|
|
211
|
+
encoding: str = "utf-8",
|
|
212
|
+
encoding_errors: str = "strict",
|
|
213
|
+
decode_responses: bool = False,
|
|
214
|
+
retry_on_timeout: bool = False,
|
|
215
|
+
retry: Retry = Retry(
|
|
216
|
+
backoff=ExponentialWithJitterBackoff(base=1, cap=10), retries=3
|
|
217
|
+
),
|
|
218
|
+
retry_on_error: Optional[List[Type[Exception]]] = None,
|
|
219
|
+
ssl: bool = False,
|
|
220
|
+
ssl_keyfile: Optional[str] = None,
|
|
221
|
+
ssl_certfile: Optional[str] = None,
|
|
222
|
+
ssl_cert_reqs: Union[str, "ssl.VerifyMode"] = "required",
|
|
223
|
+
ssl_ca_certs: Optional[str] = None,
|
|
224
|
+
ssl_ca_path: Optional[str] = None,
|
|
225
|
+
ssl_ca_data: Optional[str] = None,
|
|
226
|
+
ssl_check_hostname: bool = True,
|
|
227
|
+
ssl_password: Optional[str] = None,
|
|
228
|
+
ssl_validate_ocsp: bool = False,
|
|
229
|
+
ssl_validate_ocsp_stapled: bool = False,
|
|
230
|
+
ssl_ocsp_context: Optional["OpenSSL.SSL.Context"] = None,
|
|
231
|
+
ssl_ocsp_expected_cert: Optional[str] = None,
|
|
232
|
+
ssl_min_version: Optional["ssl.TLSVersion"] = None,
|
|
233
|
+
ssl_ciphers: Optional[str] = None,
|
|
234
|
+
max_connections: Optional[int] = None,
|
|
235
|
+
single_connection_client: bool = False,
|
|
236
|
+
health_check_interval: int = 0,
|
|
237
|
+
client_name: Optional[str] = None,
|
|
238
|
+
lib_name: Optional[str] = "redis-py",
|
|
239
|
+
lib_version: Optional[str] = get_lib_version(),
|
|
240
|
+
username: Optional[str] = None,
|
|
241
|
+
redis_connect_func: Optional[Callable[[], None]] = None,
|
|
219
242
|
credential_provider: Optional[CredentialProvider] = None,
|
|
220
243
|
protocol: Optional[int] = 2,
|
|
221
244
|
cache: Optional[CacheInterface] = None,
|
|
@@ -224,10 +247,24 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
224
247
|
) -> None:
|
|
225
248
|
"""
|
|
226
249
|
Initialize a new Redis client.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
250
|
+
|
|
251
|
+
To specify a retry policy for specific errors, you have two options:
|
|
252
|
+
|
|
253
|
+
1. Set the `retry_on_error` to a list of the error/s to retry on, and
|
|
254
|
+
you can also set `retry` to a valid `Retry` object(in case the default
|
|
255
|
+
one is not appropriate) - with this approach the retries will be triggered
|
|
256
|
+
on the default errors specified in the Retry object enriched with the
|
|
257
|
+
errors specified in `retry_on_error`.
|
|
258
|
+
|
|
259
|
+
2. Define a `Retry` object with configured 'supported_errors' and set
|
|
260
|
+
it to the `retry` parameter - with this approach you completely redefine
|
|
261
|
+
the errors on which retries will happen.
|
|
262
|
+
|
|
263
|
+
`retry_on_timeout` is deprecated - please include the TimeoutError
|
|
264
|
+
either in the Retry object or in the `retry_on_error` list.
|
|
265
|
+
|
|
266
|
+
When 'connection_pool' is provided - the retry configuration of the
|
|
267
|
+
provided pool will be used.
|
|
231
268
|
|
|
232
269
|
Args:
|
|
233
270
|
|
|
@@ -240,24 +277,8 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
240
277
|
else:
|
|
241
278
|
self._event_dispatcher = event_dispatcher
|
|
242
279
|
if not connection_pool:
|
|
243
|
-
if charset is not None:
|
|
244
|
-
warnings.warn(
|
|
245
|
-
DeprecationWarning(
|
|
246
|
-
'"charset" is deprecated. Use "encoding" instead'
|
|
247
|
-
)
|
|
248
|
-
)
|
|
249
|
-
encoding = charset
|
|
250
|
-
if errors is not None:
|
|
251
|
-
warnings.warn(
|
|
252
|
-
DeprecationWarning(
|
|
253
|
-
'"errors" is deprecated. Use "encoding_errors" instead'
|
|
254
|
-
)
|
|
255
|
-
)
|
|
256
|
-
encoding_errors = errors
|
|
257
280
|
if not retry_on_error:
|
|
258
281
|
retry_on_error = []
|
|
259
|
-
if retry_on_timeout is True:
|
|
260
|
-
retry_on_error.append(TimeoutError)
|
|
261
282
|
kwargs = {
|
|
262
283
|
"db": db,
|
|
263
284
|
"username": username,
|
|
@@ -351,7 +372,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
351
372
|
self.connection = None
|
|
352
373
|
self._single_connection_client = single_connection_client
|
|
353
374
|
if self._single_connection_client:
|
|
354
|
-
self.connection = self.connection_pool.get_connection(
|
|
375
|
+
self.connection = self.connection_pool.get_connection()
|
|
355
376
|
self._event_dispatcher.dispatch(
|
|
356
377
|
AfterSingleConnectionInstantiationEvent(
|
|
357
378
|
self.connection, ClientType.SYNC, self.single_connection_lock
|
|
@@ -379,10 +400,10 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
379
400
|
"""Get the connection's key-word arguments"""
|
|
380
401
|
return self.connection_pool.connection_kwargs
|
|
381
402
|
|
|
382
|
-
def get_retry(self) -> Optional[
|
|
403
|
+
def get_retry(self) -> Optional[Retry]:
|
|
383
404
|
return self.get_connection_kwargs().get("retry")
|
|
384
405
|
|
|
385
|
-
def set_retry(self, retry:
|
|
406
|
+
def set_retry(self, retry: Retry) -> None:
|
|
386
407
|
self.get_connection_kwargs().update({"retry": retry})
|
|
387
408
|
self.connection_pool.set_retry(retry)
|
|
388
409
|
|
|
@@ -458,6 +479,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
458
479
|
blocking_timeout: Optional[float] = None,
|
|
459
480
|
lock_class: Union[None, Any] = None,
|
|
460
481
|
thread_local: bool = True,
|
|
482
|
+
raise_on_release_error: bool = True,
|
|
461
483
|
):
|
|
462
484
|
"""
|
|
463
485
|
Return a new Lock object using key ``name`` that mimics
|
|
@@ -504,6 +526,11 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
504
526
|
thread-1 would see the token value as "xyz" and would be
|
|
505
527
|
able to successfully release the thread-2's lock.
|
|
506
528
|
|
|
529
|
+
``raise_on_release_error`` indicates whether to raise an exception when
|
|
530
|
+
the lock is no longer owned when exiting the context manager. By default,
|
|
531
|
+
this is True, meaning an exception will be raised. If False, the warning
|
|
532
|
+
will be logged and the exception will be suppressed.
|
|
533
|
+
|
|
507
534
|
In some use cases it's necessary to disable thread local storage. For
|
|
508
535
|
example, if you have code where one thread acquires a lock and passes
|
|
509
536
|
that lock instance to a worker thread to release later. If thread
|
|
@@ -521,6 +548,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
521
548
|
blocking=blocking,
|
|
522
549
|
blocking_timeout=blocking_timeout,
|
|
523
550
|
thread_local=thread_local,
|
|
551
|
+
raise_on_release_error=raise_on_release_error,
|
|
524
552
|
)
|
|
525
553
|
|
|
526
554
|
def pubsub(self, **kwargs):
|
|
@@ -548,9 +576,12 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
548
576
|
self.close()
|
|
549
577
|
|
|
550
578
|
def __del__(self):
|
|
551
|
-
|
|
579
|
+
try:
|
|
580
|
+
self.close()
|
|
581
|
+
except Exception:
|
|
582
|
+
pass
|
|
552
583
|
|
|
553
|
-
def close(self):
|
|
584
|
+
def close(self) -> None:
|
|
554
585
|
# In case a connection property does not yet exist
|
|
555
586
|
# (due to a crash earlier in the Redis() constructor), return
|
|
556
587
|
# immediately as there is nothing to clean-up.
|
|
@@ -572,18 +603,18 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
572
603
|
conn.send_command(*args, **options)
|
|
573
604
|
return self.parse_response(conn, command_name, **options)
|
|
574
605
|
|
|
575
|
-
def
|
|
606
|
+
def _close_connection(self, conn) -> None:
|
|
576
607
|
"""
|
|
577
|
-
Close the connection
|
|
578
|
-
|
|
579
|
-
|
|
608
|
+
Close the connection before retrying.
|
|
609
|
+
|
|
610
|
+
The supported exceptions are already checked in the
|
|
611
|
+
retry object so we don't need to do it here.
|
|
612
|
+
|
|
613
|
+
After we disconnect the connection, it will try to reconnect and
|
|
614
|
+
do a health check as part of the send_command logic(on connection level).
|
|
580
615
|
"""
|
|
616
|
+
|
|
581
617
|
conn.disconnect()
|
|
582
|
-
if (
|
|
583
|
-
conn.retry_on_error is None
|
|
584
|
-
or isinstance(error, tuple(conn.retry_on_error)) is False
|
|
585
|
-
):
|
|
586
|
-
raise error
|
|
587
618
|
|
|
588
619
|
# COMMAND EXECUTION AND PROTOCOL PARSING
|
|
589
620
|
def execute_command(self, *args, **options):
|
|
@@ -593,7 +624,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
593
624
|
"""Execute a command and return a parsed response"""
|
|
594
625
|
pool = self.connection_pool
|
|
595
626
|
command_name = args[0]
|
|
596
|
-
conn = self.connection or pool.get_connection(
|
|
627
|
+
conn = self.connection or pool.get_connection()
|
|
597
628
|
|
|
598
629
|
if self._single_connection_client:
|
|
599
630
|
self.single_connection_lock.acquire()
|
|
@@ -602,7 +633,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
602
633
|
lambda: self._send_command_parse_response(
|
|
603
634
|
conn, command_name, *args, **options
|
|
604
635
|
),
|
|
605
|
-
lambda
|
|
636
|
+
lambda _: self._close_connection(conn),
|
|
606
637
|
)
|
|
607
638
|
finally:
|
|
608
639
|
if self._single_connection_client:
|
|
@@ -652,7 +683,7 @@ class Monitor:
|
|
|
652
683
|
|
|
653
684
|
def __init__(self, connection_pool):
|
|
654
685
|
self.connection_pool = connection_pool
|
|
655
|
-
self.connection = self.connection_pool.get_connection(
|
|
686
|
+
self.connection = self.connection_pool.get_connection()
|
|
656
687
|
|
|
657
688
|
def __enter__(self):
|
|
658
689
|
self.connection.send_command("MONITOR")
|
|
@@ -825,9 +856,7 @@ class PubSub:
|
|
|
825
856
|
# subscribed to one or more channels
|
|
826
857
|
|
|
827
858
|
if self.connection is None:
|
|
828
|
-
self.connection = self.connection_pool.get_connection(
|
|
829
|
-
"pubsub", self.shard_hint
|
|
830
|
-
)
|
|
859
|
+
self.connection = self.connection_pool.get_connection()
|
|
831
860
|
# register a callback that re-subscribes to any channels we
|
|
832
861
|
# were listening to when we were disconnected
|
|
833
862
|
self.connection.register_connect_callback(self.on_connect)
|
|
@@ -863,19 +892,14 @@ class PubSub:
|
|
|
863
892
|
)
|
|
864
893
|
ttl -= 1
|
|
865
894
|
|
|
866
|
-
def
|
|
895
|
+
def _reconnect(self, conn) -> None:
|
|
867
896
|
"""
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
reconnect
|
|
897
|
+
The supported exceptions are already checked in the
|
|
898
|
+
retry object so we don't need to do it here.
|
|
899
|
+
|
|
900
|
+
In this error handler we are trying to reconnect to the server.
|
|
872
901
|
"""
|
|
873
902
|
conn.disconnect()
|
|
874
|
-
if (
|
|
875
|
-
conn.retry_on_error is None
|
|
876
|
-
or isinstance(error, tuple(conn.retry_on_error)) is False
|
|
877
|
-
):
|
|
878
|
-
raise error
|
|
879
903
|
conn.connect()
|
|
880
904
|
|
|
881
905
|
def _execute(self, conn, command, *args, **kwargs):
|
|
@@ -888,7 +912,7 @@ class PubSub:
|
|
|
888
912
|
"""
|
|
889
913
|
return conn.retry.call_with_retry(
|
|
890
914
|
lambda: command(*args, **kwargs),
|
|
891
|
-
lambda
|
|
915
|
+
lambda _: self._reconnect(conn),
|
|
892
916
|
)
|
|
893
917
|
|
|
894
918
|
def parse_response(self, block=True, timeout=0):
|
|
@@ -937,7 +961,7 @@ class PubSub:
|
|
|
937
961
|
"did you forget to call subscribe() or psubscribe()?"
|
|
938
962
|
)
|
|
939
963
|
|
|
940
|
-
if conn.health_check_interval and time.
|
|
964
|
+
if conn.health_check_interval and time.monotonic() > conn.next_health_check:
|
|
941
965
|
conn.send_command("PING", self.HEALTH_CHECK_MESSAGE, check_health=False)
|
|
942
966
|
self.health_check_response_counter += 1
|
|
943
967
|
|
|
@@ -1087,12 +1111,12 @@ class PubSub:
|
|
|
1087
1111
|
"""
|
|
1088
1112
|
if not self.subscribed:
|
|
1089
1113
|
# Wait for subscription
|
|
1090
|
-
start_time = time.
|
|
1114
|
+
start_time = time.monotonic()
|
|
1091
1115
|
if self.subscribed_event.wait(timeout) is True:
|
|
1092
1116
|
# The connection was subscribed during the timeout time frame.
|
|
1093
1117
|
# The timeout should be adjusted based on the time spent
|
|
1094
1118
|
# waiting for the subscription
|
|
1095
|
-
time_spent = time.
|
|
1119
|
+
time_spent = time.monotonic() - start_time
|
|
1096
1120
|
timeout = max(0.0, timeout - time_spent)
|
|
1097
1121
|
else:
|
|
1098
1122
|
# The connection isn't subscribed to any channels or patterns,
|
|
@@ -1257,7 +1281,8 @@ class Pipeline(Redis):
|
|
|
1257
1281
|
in one transmission. This is convenient for batch processing, such as
|
|
1258
1282
|
saving all the values in a list to Redis.
|
|
1259
1283
|
|
|
1260
|
-
All commands executed within a pipeline
|
|
1284
|
+
All commands executed within a pipeline(when running in transactional mode,
|
|
1285
|
+
which is the default behavior) are wrapped with MULTI and EXEC
|
|
1261
1286
|
calls. This guarantees all commands executed in the pipeline will be
|
|
1262
1287
|
executed atomically.
|
|
1263
1288
|
|
|
@@ -1278,9 +1303,10 @@ class Pipeline(Redis):
|
|
|
1278
1303
|
self.response_callbacks = response_callbacks
|
|
1279
1304
|
self.transaction = transaction
|
|
1280
1305
|
self.shard_hint = shard_hint
|
|
1281
|
-
|
|
1282
1306
|
self.watching = False
|
|
1283
|
-
self.
|
|
1307
|
+
self.command_stack = []
|
|
1308
|
+
self.scripts: Set[Script] = set()
|
|
1309
|
+
self.explicit_transaction = False
|
|
1284
1310
|
|
|
1285
1311
|
def __enter__(self) -> "Pipeline":
|
|
1286
1312
|
return self
|
|
@@ -1346,50 +1372,51 @@ class Pipeline(Redis):
|
|
|
1346
1372
|
return self.immediate_execute_command(*args, **kwargs)
|
|
1347
1373
|
return self.pipeline_execute_command(*args, **kwargs)
|
|
1348
1374
|
|
|
1349
|
-
def
|
|
1375
|
+
def _disconnect_reset_raise_on_watching(
|
|
1376
|
+
self,
|
|
1377
|
+
conn: AbstractConnection,
|
|
1378
|
+
error: Exception,
|
|
1379
|
+
) -> None:
|
|
1350
1380
|
"""
|
|
1351
|
-
Close the connection
|
|
1352
|
-
raise an exception if we were watching
|
|
1353
|
-
|
|
1354
|
-
|
|
1381
|
+
Close the connection reset watching state and
|
|
1382
|
+
raise an exception if we were watching.
|
|
1383
|
+
|
|
1384
|
+
The supported exceptions are already checked in the
|
|
1385
|
+
retry object so we don't need to do it here.
|
|
1386
|
+
|
|
1387
|
+
After we disconnect the connection, it will try to reconnect and
|
|
1388
|
+
do a health check as part of the send_command logic(on connection level).
|
|
1355
1389
|
"""
|
|
1356
1390
|
conn.disconnect()
|
|
1391
|
+
|
|
1357
1392
|
# if we were already watching a variable, the watch is no longer
|
|
1358
1393
|
# valid since this connection has died. raise a WatchError, which
|
|
1359
1394
|
# indicates the user should retry this transaction.
|
|
1360
1395
|
if self.watching:
|
|
1361
1396
|
self.reset()
|
|
1362
1397
|
raise WatchError(
|
|
1363
|
-
"A
|
|
1398
|
+
f"A {type(error).__name__} occurred while watching one or more keys"
|
|
1364
1399
|
)
|
|
1365
|
-
# if retry_on_error is not set or the error is not one
|
|
1366
|
-
# of the specified error types, raise it
|
|
1367
|
-
if (
|
|
1368
|
-
conn.retry_on_error is None
|
|
1369
|
-
or isinstance(error, tuple(conn.retry_on_error)) is False
|
|
1370
|
-
):
|
|
1371
|
-
self.reset()
|
|
1372
|
-
raise
|
|
1373
1400
|
|
|
1374
1401
|
def immediate_execute_command(self, *args, **options):
|
|
1375
1402
|
"""
|
|
1376
|
-
Execute a command immediately, but don't auto-retry on
|
|
1377
|
-
|
|
1378
|
-
issuing WATCH or subsequent commands retrieving their values but before
|
|
1403
|
+
Execute a command immediately, but don't auto-retry on the supported
|
|
1404
|
+
errors for retry if we're already WATCHing a variable.
|
|
1405
|
+
Used when issuing WATCH or subsequent commands retrieving their values but before
|
|
1379
1406
|
MULTI is called.
|
|
1380
1407
|
"""
|
|
1381
1408
|
command_name = args[0]
|
|
1382
1409
|
conn = self.connection
|
|
1383
1410
|
# if this is the first call, we need a connection
|
|
1384
1411
|
if not conn:
|
|
1385
|
-
conn = self.connection_pool.get_connection(
|
|
1412
|
+
conn = self.connection_pool.get_connection()
|
|
1386
1413
|
self.connection = conn
|
|
1387
1414
|
|
|
1388
1415
|
return conn.retry.call_with_retry(
|
|
1389
1416
|
lambda: self._send_command_parse_response(
|
|
1390
1417
|
conn, command_name, *args, **options
|
|
1391
1418
|
),
|
|
1392
|
-
lambda error: self.
|
|
1419
|
+
lambda error: self._disconnect_reset_raise_on_watching(conn, error),
|
|
1393
1420
|
)
|
|
1394
1421
|
|
|
1395
1422
|
def pipeline_execute_command(self, *args, **options) -> "Pipeline":
|
|
@@ -1501,7 +1528,7 @@ class Pipeline(Redis):
|
|
|
1501
1528
|
def annotate_exception(self, exception, number, command):
|
|
1502
1529
|
cmd = " ".join(map(safe_str, command))
|
|
1503
1530
|
msg = (
|
|
1504
|
-
f"Command # {number} ({cmd}) of pipeline "
|
|
1531
|
+
f"Command # {number} ({truncate_text(cmd)}) of pipeline "
|
|
1505
1532
|
f"caused error: {exception.args[0]}"
|
|
1506
1533
|
)
|
|
1507
1534
|
exception.args = (msg,) + exception.args[1:]
|
|
@@ -1527,15 +1554,19 @@ class Pipeline(Redis):
|
|
|
1527
1554
|
if not exist:
|
|
1528
1555
|
s.sha = immediate("SCRIPT LOAD", s.script)
|
|
1529
1556
|
|
|
1530
|
-
def
|
|
1557
|
+
def _disconnect_raise_on_watching(
|
|
1531
1558
|
self,
|
|
1532
1559
|
conn: AbstractConnection,
|
|
1533
1560
|
error: Exception,
|
|
1534
1561
|
) -> None:
|
|
1535
1562
|
"""
|
|
1536
|
-
Close the connection, raise an exception if we were watching
|
|
1537
|
-
|
|
1538
|
-
|
|
1563
|
+
Close the connection, raise an exception if we were watching.
|
|
1564
|
+
|
|
1565
|
+
The supported exceptions are already checked in the
|
|
1566
|
+
retry object so we don't need to do it here.
|
|
1567
|
+
|
|
1568
|
+
After we disconnect the connection, it will try to reconnect and
|
|
1569
|
+
do a health check as part of the send_command logic(on connection level).
|
|
1539
1570
|
"""
|
|
1540
1571
|
conn.disconnect()
|
|
1541
1572
|
# if we were watching a variable, the watch is no longer valid
|
|
@@ -1543,19 +1574,10 @@ class Pipeline(Redis):
|
|
|
1543
1574
|
# indicates the user should retry this transaction.
|
|
1544
1575
|
if self.watching:
|
|
1545
1576
|
raise WatchError(
|
|
1546
|
-
"A
|
|
1577
|
+
f"A {type(error).__name__} occurred while watching one or more keys"
|
|
1547
1578
|
)
|
|
1548
|
-
# if retry_on_error is not set or the error is not one
|
|
1549
|
-
# of the specified error types, raise it
|
|
1550
|
-
if (
|
|
1551
|
-
conn.retry_on_error is None
|
|
1552
|
-
or isinstance(error, tuple(conn.retry_on_error)) is False
|
|
1553
|
-
):
|
|
1554
|
-
|
|
1555
|
-
self.reset()
|
|
1556
|
-
raise error
|
|
1557
1579
|
|
|
1558
|
-
def execute(self, raise_on_error=True):
|
|
1580
|
+
def execute(self, raise_on_error: bool = True) -> List[Any]:
|
|
1559
1581
|
"""Execute all the commands in the current pipeline"""
|
|
1560
1582
|
stack = self.command_stack
|
|
1561
1583
|
if not stack and not self.watching:
|
|
@@ -1569,7 +1591,7 @@ class Pipeline(Redis):
|
|
|
1569
1591
|
|
|
1570
1592
|
conn = self.connection
|
|
1571
1593
|
if not conn:
|
|
1572
|
-
conn = self.connection_pool.get_connection(
|
|
1594
|
+
conn = self.connection_pool.get_connection()
|
|
1573
1595
|
# assign to self.connection so reset() releases the connection
|
|
1574
1596
|
# back to the pool after we're done
|
|
1575
1597
|
self.connection = conn
|
|
@@ -1577,7 +1599,7 @@ class Pipeline(Redis):
|
|
|
1577
1599
|
try:
|
|
1578
1600
|
return conn.retry.call_with_retry(
|
|
1579
1601
|
lambda: execute(conn, stack, raise_on_error),
|
|
1580
|
-
lambda error: self.
|
|
1602
|
+
lambda error: self._disconnect_raise_on_watching(conn, error),
|
|
1581
1603
|
)
|
|
1582
1604
|
finally:
|
|
1583
1605
|
self.reset()
|