redis 5.3.0b5__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 +102 -82
- 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 +106 -98
- redis/cluster.py +208 -79
- 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 +78 -29
- 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/utils.py +114 -1
- {redis-5.3.0b5.dist-info → redis-6.0.0.dist-info}/METADATA +57 -23
- redis-6.0.0.dist-info/RECORD +78 -0
- {redis-5.3.0b5.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.0b5.dist-info/RECORD +0 -82
- redis-5.3.0b5.dist-info/top_level.txt +0 -1
- /redis/commands/search/{indexDefinition.py → index_definition.py} +0 -0
- {redis-5.3.0b5.dist-info → redis-6.0.0.dist-info/licenses}/LICENSE +0 -0
redis/client.py
CHANGED
|
@@ -2,7 +2,6 @@ import copy
|
|
|
2
2
|
import re
|
|
3
3
|
import threading
|
|
4
4
|
import time
|
|
5
|
-
import warnings
|
|
6
5
|
from itertools import chain
|
|
7
6
|
from typing import (
|
|
8
7
|
TYPE_CHECKING,
|
|
@@ -12,6 +11,7 @@ from typing import (
|
|
|
12
11
|
List,
|
|
13
12
|
Mapping,
|
|
14
13
|
Optional,
|
|
14
|
+
Set,
|
|
15
15
|
Type,
|
|
16
16
|
Union,
|
|
17
17
|
)
|
|
@@ -23,6 +23,7 @@ from redis._parsers.helpers import (
|
|
|
23
23
|
_RedisCallbacksRESP3,
|
|
24
24
|
bool_ok,
|
|
25
25
|
)
|
|
26
|
+
from redis.backoff import ExponentialWithJitterBackoff
|
|
26
27
|
from redis.cache import CacheConfig, CacheInterface
|
|
27
28
|
from redis.commands import (
|
|
28
29
|
CoreCommands,
|
|
@@ -30,6 +31,7 @@ from redis.commands import (
|
|
|
30
31
|
SentinelCommands,
|
|
31
32
|
list_or_args,
|
|
32
33
|
)
|
|
34
|
+
from redis.commands.core import Script
|
|
33
35
|
from redis.connection import (
|
|
34
36
|
AbstractConnection,
|
|
35
37
|
ConnectionPool,
|
|
@@ -50,7 +52,6 @@ from redis.exceptions import (
|
|
|
50
52
|
PubSubError,
|
|
51
53
|
RedisError,
|
|
52
54
|
ResponseError,
|
|
53
|
-
TimeoutError,
|
|
54
55
|
WatchError,
|
|
55
56
|
)
|
|
56
57
|
from redis.lock import Lock
|
|
@@ -58,9 +59,11 @@ from redis.retry import Retry
|
|
|
58
59
|
from redis.utils import (
|
|
59
60
|
HIREDIS_AVAILABLE,
|
|
60
61
|
_set_info_logger,
|
|
62
|
+
deprecated_args,
|
|
61
63
|
get_lib_version,
|
|
62
64
|
safe_str,
|
|
63
65
|
str_if_bytes,
|
|
66
|
+
truncate_text,
|
|
64
67
|
)
|
|
65
68
|
|
|
66
69
|
if TYPE_CHECKING:
|
|
@@ -188,6 +191,11 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
188
191
|
client.auto_close_connection_pool = True
|
|
189
192
|
return client
|
|
190
193
|
|
|
194
|
+
@deprecated_args(
|
|
195
|
+
args_to_warn=["retry_on_timeout"],
|
|
196
|
+
reason="TimeoutError is included by default.",
|
|
197
|
+
version="6.0.0",
|
|
198
|
+
)
|
|
191
199
|
def __init__(
|
|
192
200
|
self,
|
|
193
201
|
host: str = "localhost",
|
|
@@ -202,19 +210,20 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
202
210
|
unix_socket_path: Optional[str] = None,
|
|
203
211
|
encoding: str = "utf-8",
|
|
204
212
|
encoding_errors: str = "strict",
|
|
205
|
-
charset: Optional[str] = None,
|
|
206
|
-
errors: Optional[str] = None,
|
|
207
213
|
decode_responses: bool = False,
|
|
208
214
|
retry_on_timeout: bool = False,
|
|
215
|
+
retry: Retry = Retry(
|
|
216
|
+
backoff=ExponentialWithJitterBackoff(base=1, cap=10), retries=3
|
|
217
|
+
),
|
|
209
218
|
retry_on_error: Optional[List[Type[Exception]]] = None,
|
|
210
219
|
ssl: bool = False,
|
|
211
220
|
ssl_keyfile: Optional[str] = None,
|
|
212
221
|
ssl_certfile: Optional[str] = None,
|
|
213
|
-
ssl_cert_reqs: str = "required",
|
|
222
|
+
ssl_cert_reqs: Union[str, "ssl.VerifyMode"] = "required",
|
|
214
223
|
ssl_ca_certs: Optional[str] = None,
|
|
215
224
|
ssl_ca_path: Optional[str] = None,
|
|
216
225
|
ssl_ca_data: Optional[str] = None,
|
|
217
|
-
ssl_check_hostname: bool =
|
|
226
|
+
ssl_check_hostname: bool = True,
|
|
218
227
|
ssl_password: Optional[str] = None,
|
|
219
228
|
ssl_validate_ocsp: bool = False,
|
|
220
229
|
ssl_validate_ocsp_stapled: bool = False,
|
|
@@ -229,7 +238,6 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
229
238
|
lib_name: Optional[str] = "redis-py",
|
|
230
239
|
lib_version: Optional[str] = get_lib_version(),
|
|
231
240
|
username: Optional[str] = None,
|
|
232
|
-
retry: Optional[Retry] = None,
|
|
233
241
|
redis_connect_func: Optional[Callable[[], None]] = None,
|
|
234
242
|
credential_provider: Optional[CredentialProvider] = None,
|
|
235
243
|
protocol: Optional[int] = 2,
|
|
@@ -239,10 +247,24 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
239
247
|
) -> None:
|
|
240
248
|
"""
|
|
241
249
|
Initialize a new Redis client.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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.
|
|
246
268
|
|
|
247
269
|
Args:
|
|
248
270
|
|
|
@@ -255,24 +277,8 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
255
277
|
else:
|
|
256
278
|
self._event_dispatcher = event_dispatcher
|
|
257
279
|
if not connection_pool:
|
|
258
|
-
if charset is not None:
|
|
259
|
-
warnings.warn(
|
|
260
|
-
DeprecationWarning(
|
|
261
|
-
'"charset" is deprecated. Use "encoding" instead'
|
|
262
|
-
)
|
|
263
|
-
)
|
|
264
|
-
encoding = charset
|
|
265
|
-
if errors is not None:
|
|
266
|
-
warnings.warn(
|
|
267
|
-
DeprecationWarning(
|
|
268
|
-
'"errors" is deprecated. Use "encoding_errors" instead'
|
|
269
|
-
)
|
|
270
|
-
)
|
|
271
|
-
encoding_errors = errors
|
|
272
280
|
if not retry_on_error:
|
|
273
281
|
retry_on_error = []
|
|
274
|
-
if retry_on_timeout is True:
|
|
275
|
-
retry_on_error.append(TimeoutError)
|
|
276
282
|
kwargs = {
|
|
277
283
|
"db": db,
|
|
278
284
|
"username": username,
|
|
@@ -366,7 +372,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
366
372
|
self.connection = None
|
|
367
373
|
self._single_connection_client = single_connection_client
|
|
368
374
|
if self._single_connection_client:
|
|
369
|
-
self.connection = self.connection_pool.get_connection(
|
|
375
|
+
self.connection = self.connection_pool.get_connection()
|
|
370
376
|
self._event_dispatcher.dispatch(
|
|
371
377
|
AfterSingleConnectionInstantiationEvent(
|
|
372
378
|
self.connection, ClientType.SYNC, self.single_connection_lock
|
|
@@ -394,10 +400,10 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
394
400
|
"""Get the connection's key-word arguments"""
|
|
395
401
|
return self.connection_pool.connection_kwargs
|
|
396
402
|
|
|
397
|
-
def get_retry(self) -> Optional[
|
|
403
|
+
def get_retry(self) -> Optional[Retry]:
|
|
398
404
|
return self.get_connection_kwargs().get("retry")
|
|
399
405
|
|
|
400
|
-
def set_retry(self, retry:
|
|
406
|
+
def set_retry(self, retry: Retry) -> None:
|
|
401
407
|
self.get_connection_kwargs().update({"retry": retry})
|
|
402
408
|
self.connection_pool.set_retry(retry)
|
|
403
409
|
|
|
@@ -473,6 +479,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
473
479
|
blocking_timeout: Optional[float] = None,
|
|
474
480
|
lock_class: Union[None, Any] = None,
|
|
475
481
|
thread_local: bool = True,
|
|
482
|
+
raise_on_release_error: bool = True,
|
|
476
483
|
):
|
|
477
484
|
"""
|
|
478
485
|
Return a new Lock object using key ``name`` that mimics
|
|
@@ -519,6 +526,11 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
519
526
|
thread-1 would see the token value as "xyz" and would be
|
|
520
527
|
able to successfully release the thread-2's lock.
|
|
521
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
|
+
|
|
522
534
|
In some use cases it's necessary to disable thread local storage. For
|
|
523
535
|
example, if you have code where one thread acquires a lock and passes
|
|
524
536
|
that lock instance to a worker thread to release later. If thread
|
|
@@ -536,6 +548,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
536
548
|
blocking=blocking,
|
|
537
549
|
blocking_timeout=blocking_timeout,
|
|
538
550
|
thread_local=thread_local,
|
|
551
|
+
raise_on_release_error=raise_on_release_error,
|
|
539
552
|
)
|
|
540
553
|
|
|
541
554
|
def pubsub(self, **kwargs):
|
|
@@ -563,7 +576,10 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
563
576
|
self.close()
|
|
564
577
|
|
|
565
578
|
def __del__(self):
|
|
566
|
-
|
|
579
|
+
try:
|
|
580
|
+
self.close()
|
|
581
|
+
except Exception:
|
|
582
|
+
pass
|
|
567
583
|
|
|
568
584
|
def close(self) -> None:
|
|
569
585
|
# In case a connection property does not yet exist
|
|
@@ -587,18 +603,18 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
587
603
|
conn.send_command(*args, **options)
|
|
588
604
|
return self.parse_response(conn, command_name, **options)
|
|
589
605
|
|
|
590
|
-
def
|
|
606
|
+
def _close_connection(self, conn) -> None:
|
|
591
607
|
"""
|
|
592
|
-
Close the connection
|
|
593
|
-
|
|
594
|
-
|
|
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).
|
|
595
615
|
"""
|
|
616
|
+
|
|
596
617
|
conn.disconnect()
|
|
597
|
-
if (
|
|
598
|
-
conn.retry_on_error is None
|
|
599
|
-
or isinstance(error, tuple(conn.retry_on_error)) is False
|
|
600
|
-
):
|
|
601
|
-
raise error
|
|
602
618
|
|
|
603
619
|
# COMMAND EXECUTION AND PROTOCOL PARSING
|
|
604
620
|
def execute_command(self, *args, **options):
|
|
@@ -608,7 +624,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
608
624
|
"""Execute a command and return a parsed response"""
|
|
609
625
|
pool = self.connection_pool
|
|
610
626
|
command_name = args[0]
|
|
611
|
-
conn = self.connection or pool.get_connection(
|
|
627
|
+
conn = self.connection or pool.get_connection()
|
|
612
628
|
|
|
613
629
|
if self._single_connection_client:
|
|
614
630
|
self.single_connection_lock.acquire()
|
|
@@ -617,7 +633,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
617
633
|
lambda: self._send_command_parse_response(
|
|
618
634
|
conn, command_name, *args, **options
|
|
619
635
|
),
|
|
620
|
-
lambda
|
|
636
|
+
lambda _: self._close_connection(conn),
|
|
621
637
|
)
|
|
622
638
|
finally:
|
|
623
639
|
if self._single_connection_client:
|
|
@@ -667,7 +683,7 @@ class Monitor:
|
|
|
667
683
|
|
|
668
684
|
def __init__(self, connection_pool):
|
|
669
685
|
self.connection_pool = connection_pool
|
|
670
|
-
self.connection = self.connection_pool.get_connection(
|
|
686
|
+
self.connection = self.connection_pool.get_connection()
|
|
671
687
|
|
|
672
688
|
def __enter__(self):
|
|
673
689
|
self.connection.send_command("MONITOR")
|
|
@@ -840,9 +856,7 @@ class PubSub:
|
|
|
840
856
|
# subscribed to one or more channels
|
|
841
857
|
|
|
842
858
|
if self.connection is None:
|
|
843
|
-
self.connection = self.connection_pool.get_connection(
|
|
844
|
-
"pubsub", self.shard_hint
|
|
845
|
-
)
|
|
859
|
+
self.connection = self.connection_pool.get_connection()
|
|
846
860
|
# register a callback that re-subscribes to any channels we
|
|
847
861
|
# were listening to when we were disconnected
|
|
848
862
|
self.connection.register_connect_callback(self.on_connect)
|
|
@@ -878,19 +892,14 @@ class PubSub:
|
|
|
878
892
|
)
|
|
879
893
|
ttl -= 1
|
|
880
894
|
|
|
881
|
-
def
|
|
895
|
+
def _reconnect(self, conn) -> None:
|
|
882
896
|
"""
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
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.
|
|
887
901
|
"""
|
|
888
902
|
conn.disconnect()
|
|
889
|
-
if (
|
|
890
|
-
conn.retry_on_error is None
|
|
891
|
-
or isinstance(error, tuple(conn.retry_on_error)) is False
|
|
892
|
-
):
|
|
893
|
-
raise error
|
|
894
903
|
conn.connect()
|
|
895
904
|
|
|
896
905
|
def _execute(self, conn, command, *args, **kwargs):
|
|
@@ -903,7 +912,7 @@ class PubSub:
|
|
|
903
912
|
"""
|
|
904
913
|
return conn.retry.call_with_retry(
|
|
905
914
|
lambda: command(*args, **kwargs),
|
|
906
|
-
lambda
|
|
915
|
+
lambda _: self._reconnect(conn),
|
|
907
916
|
)
|
|
908
917
|
|
|
909
918
|
def parse_response(self, block=True, timeout=0):
|
|
@@ -952,7 +961,7 @@ class PubSub:
|
|
|
952
961
|
"did you forget to call subscribe() or psubscribe()?"
|
|
953
962
|
)
|
|
954
963
|
|
|
955
|
-
if conn.health_check_interval and time.
|
|
964
|
+
if conn.health_check_interval and time.monotonic() > conn.next_health_check:
|
|
956
965
|
conn.send_command("PING", self.HEALTH_CHECK_MESSAGE, check_health=False)
|
|
957
966
|
self.health_check_response_counter += 1
|
|
958
967
|
|
|
@@ -1102,12 +1111,12 @@ class PubSub:
|
|
|
1102
1111
|
"""
|
|
1103
1112
|
if not self.subscribed:
|
|
1104
1113
|
# Wait for subscription
|
|
1105
|
-
start_time = time.
|
|
1114
|
+
start_time = time.monotonic()
|
|
1106
1115
|
if self.subscribed_event.wait(timeout) is True:
|
|
1107
1116
|
# The connection was subscribed during the timeout time frame.
|
|
1108
1117
|
# The timeout should be adjusted based on the time spent
|
|
1109
1118
|
# waiting for the subscription
|
|
1110
|
-
time_spent = time.
|
|
1119
|
+
time_spent = time.monotonic() - start_time
|
|
1111
1120
|
timeout = max(0.0, timeout - time_spent)
|
|
1112
1121
|
else:
|
|
1113
1122
|
# The connection isn't subscribed to any channels or patterns,
|
|
@@ -1272,7 +1281,8 @@ class Pipeline(Redis):
|
|
|
1272
1281
|
in one transmission. This is convenient for batch processing, such as
|
|
1273
1282
|
saving all the values in a list to Redis.
|
|
1274
1283
|
|
|
1275
|
-
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
|
|
1276
1286
|
calls. This guarantees all commands executed in the pipeline will be
|
|
1277
1287
|
executed atomically.
|
|
1278
1288
|
|
|
@@ -1293,9 +1303,10 @@ class Pipeline(Redis):
|
|
|
1293
1303
|
self.response_callbacks = response_callbacks
|
|
1294
1304
|
self.transaction = transaction
|
|
1295
1305
|
self.shard_hint = shard_hint
|
|
1296
|
-
|
|
1297
1306
|
self.watching = False
|
|
1298
|
-
self.
|
|
1307
|
+
self.command_stack = []
|
|
1308
|
+
self.scripts: Set[Script] = set()
|
|
1309
|
+
self.explicit_transaction = False
|
|
1299
1310
|
|
|
1300
1311
|
def __enter__(self) -> "Pipeline":
|
|
1301
1312
|
return self
|
|
@@ -1361,50 +1372,51 @@ class Pipeline(Redis):
|
|
|
1361
1372
|
return self.immediate_execute_command(*args, **kwargs)
|
|
1362
1373
|
return self.pipeline_execute_command(*args, **kwargs)
|
|
1363
1374
|
|
|
1364
|
-
def
|
|
1375
|
+
def _disconnect_reset_raise_on_watching(
|
|
1376
|
+
self,
|
|
1377
|
+
conn: AbstractConnection,
|
|
1378
|
+
error: Exception,
|
|
1379
|
+
) -> None:
|
|
1365
1380
|
"""
|
|
1366
|
-
Close the connection
|
|
1367
|
-
raise an exception if we were watching
|
|
1368
|
-
|
|
1369
|
-
|
|
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).
|
|
1370
1389
|
"""
|
|
1371
1390
|
conn.disconnect()
|
|
1391
|
+
|
|
1372
1392
|
# if we were already watching a variable, the watch is no longer
|
|
1373
1393
|
# valid since this connection has died. raise a WatchError, which
|
|
1374
1394
|
# indicates the user should retry this transaction.
|
|
1375
1395
|
if self.watching:
|
|
1376
1396
|
self.reset()
|
|
1377
1397
|
raise WatchError(
|
|
1378
|
-
"A
|
|
1398
|
+
f"A {type(error).__name__} occurred while watching one or more keys"
|
|
1379
1399
|
)
|
|
1380
|
-
# if retry_on_error is not set or the error is not one
|
|
1381
|
-
# of the specified error types, raise it
|
|
1382
|
-
if (
|
|
1383
|
-
conn.retry_on_error is None
|
|
1384
|
-
or isinstance(error, tuple(conn.retry_on_error)) is False
|
|
1385
|
-
):
|
|
1386
|
-
self.reset()
|
|
1387
|
-
raise
|
|
1388
1400
|
|
|
1389
1401
|
def immediate_execute_command(self, *args, **options):
|
|
1390
1402
|
"""
|
|
1391
|
-
Execute a command immediately, but don't auto-retry on
|
|
1392
|
-
|
|
1393
|
-
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
|
|
1394
1406
|
MULTI is called.
|
|
1395
1407
|
"""
|
|
1396
1408
|
command_name = args[0]
|
|
1397
1409
|
conn = self.connection
|
|
1398
1410
|
# if this is the first call, we need a connection
|
|
1399
1411
|
if not conn:
|
|
1400
|
-
conn = self.connection_pool.get_connection(
|
|
1412
|
+
conn = self.connection_pool.get_connection()
|
|
1401
1413
|
self.connection = conn
|
|
1402
1414
|
|
|
1403
1415
|
return conn.retry.call_with_retry(
|
|
1404
1416
|
lambda: self._send_command_parse_response(
|
|
1405
1417
|
conn, command_name, *args, **options
|
|
1406
1418
|
),
|
|
1407
|
-
lambda error: self.
|
|
1419
|
+
lambda error: self._disconnect_reset_raise_on_watching(conn, error),
|
|
1408
1420
|
)
|
|
1409
1421
|
|
|
1410
1422
|
def pipeline_execute_command(self, *args, **options) -> "Pipeline":
|
|
@@ -1516,7 +1528,7 @@ class Pipeline(Redis):
|
|
|
1516
1528
|
def annotate_exception(self, exception, number, command):
|
|
1517
1529
|
cmd = " ".join(map(safe_str, command))
|
|
1518
1530
|
msg = (
|
|
1519
|
-
f"Command # {number} ({cmd}) of pipeline "
|
|
1531
|
+
f"Command # {number} ({truncate_text(cmd)}) of pipeline "
|
|
1520
1532
|
f"caused error: {exception.args[0]}"
|
|
1521
1533
|
)
|
|
1522
1534
|
exception.args = (msg,) + exception.args[1:]
|
|
@@ -1542,15 +1554,19 @@ class Pipeline(Redis):
|
|
|
1542
1554
|
if not exist:
|
|
1543
1555
|
s.sha = immediate("SCRIPT LOAD", s.script)
|
|
1544
1556
|
|
|
1545
|
-
def
|
|
1557
|
+
def _disconnect_raise_on_watching(
|
|
1546
1558
|
self,
|
|
1547
1559
|
conn: AbstractConnection,
|
|
1548
1560
|
error: Exception,
|
|
1549
1561
|
) -> None:
|
|
1550
1562
|
"""
|
|
1551
|
-
Close the connection, raise an exception if we were watching
|
|
1552
|
-
|
|
1553
|
-
|
|
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).
|
|
1554
1570
|
"""
|
|
1555
1571
|
conn.disconnect()
|
|
1556
1572
|
# if we were watching a variable, the watch is no longer valid
|
|
@@ -1558,16 +1574,8 @@ class Pipeline(Redis):
|
|
|
1558
1574
|
# indicates the user should retry this transaction.
|
|
1559
1575
|
if self.watching:
|
|
1560
1576
|
raise WatchError(
|
|
1561
|
-
"A
|
|
1577
|
+
f"A {type(error).__name__} occurred while watching one or more keys"
|
|
1562
1578
|
)
|
|
1563
|
-
# if retry_on_error is not set or the error is not one
|
|
1564
|
-
# of the specified error types, raise it
|
|
1565
|
-
if (
|
|
1566
|
-
conn.retry_on_error is None
|
|
1567
|
-
or isinstance(error, tuple(conn.retry_on_error)) is False
|
|
1568
|
-
):
|
|
1569
|
-
self.reset()
|
|
1570
|
-
raise error
|
|
1571
1579
|
|
|
1572
1580
|
def execute(self, raise_on_error: bool = True) -> List[Any]:
|
|
1573
1581
|
"""Execute all the commands in the current pipeline"""
|
|
@@ -1583,7 +1591,7 @@ class Pipeline(Redis):
|
|
|
1583
1591
|
|
|
1584
1592
|
conn = self.connection
|
|
1585
1593
|
if not conn:
|
|
1586
|
-
conn = self.connection_pool.get_connection(
|
|
1594
|
+
conn = self.connection_pool.get_connection()
|
|
1587
1595
|
# assign to self.connection so reset() releases the connection
|
|
1588
1596
|
# back to the pool after we're done
|
|
1589
1597
|
self.connection = conn
|
|
@@ -1591,7 +1599,7 @@ class Pipeline(Redis):
|
|
|
1591
1599
|
try:
|
|
1592
1600
|
return conn.retry.call_with_retry(
|
|
1593
1601
|
lambda: execute(conn, stack, raise_on_error),
|
|
1594
|
-
lambda error: self.
|
|
1602
|
+
lambda error: self._disconnect_raise_on_watching(conn, error),
|
|
1595
1603
|
)
|
|
1596
1604
|
finally:
|
|
1597
1605
|
self.reset()
|