Skip to content

Commit cd6b09a

Browse files
committed
Updating docs related to retries and read_from_replicas.
1 parent 56b6176 commit cd6b09a

File tree

3 files changed

+39
-25
lines changed

3 files changed

+39
-25
lines changed

docs/clustering.rst

+7-4
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ When a ClusterPubSub instance is created without specifying a node, a
187187
single node will be transparently chosen for the pubsub connection on
188188
the first command execution. The node will be determined by: 1. Hashing
189189
the channel name in the request to find its keyslot 2. Selecting a node
190-
that handles the keyslot: If read_from_replicas is set to true, a
191-
replica can be selected.
190+
that handles the keyslot: If read_from_replicas is set to true or
191+
load_balancing_strategy is provided, a replica can be selected.
192192
193193
Known PubSub Limitations
194194
------------------------
@@ -216,9 +216,12 @@ By default, Redis Cluster always returns MOVE redirection response on
216216
accessing a replica node. You can overcome this limitation and scale
217217
read commands by triggering READONLY mode.
218218
219-
To enable READONLY mode pass read_from_replicas=True to RedisCluster
220-
constructor. When set to true, read commands will be assigned between
219+
To enable READONLY mode pass read_from_replicas=True or define
220+
a load_balancing_strategy to RedisCluster constructor.
221+
When read_from_replicas is set to true read commands will be assigned between
221222
the primary and its replications in a Round-Robin manner.
223+
With load_balancing_strategy you can define a custom strategy for
224+
assigning read commands to the replicas and primary nodes.
222225
223226
READONLY mode can be set at runtime by calling the readonly() method
224227
with target_nodes=‘replicas’, and read-write access can be restored by

docs/retry.rst

+29-20
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,25 @@ Retry in Redis Standalone
1313
>>> from redis.client import Redis
1414
>>> from redis.exceptions import (
1515
>>> BusyLoadingError,
16-
>>> ConnectionError,
17-
>>> TimeoutError
16+
>>> RedisError,
1817
>>> )
1918
>>>
2019
>>> # Run 3 retries with exponential backoff strategy
2120
>>> retry = Retry(ExponentialBackoff(), 3)
22-
>>> # Redis client with retries on custom errors
23-
>>> r = Redis(host='localhost', port=6379, retry=retry, retry_on_error=[BusyLoadingError, ConnectionError, TimeoutError])
24-
>>> # Redis client with retries on TimeoutError only
25-
>>> r_only_timeout = Redis(host='localhost', port=6379, retry=retry, retry_on_timeout=True)
21+
>>> # Redis client with retries on custom errors in addition to the errors
22+
>>> # that are already retried by default
23+
>>> r = Redis(host='localhost', port=6379, retry=retry, retry_on_error=[BusyLoadingError, RedisError])
2624

27-
As you can see from the example above, Redis client supports 3 parameters to configure the retry behaviour:
25+
As you can see from the example above, Redis client supports 2 parameters to configure the retry behaviour:
2826

2927
* ``retry``: :class:`~.Retry` instance with a :ref:`backoff-label` strategy and the max number of retries
30-
* ``retry_on_error``: list of :ref:`exceptions-label` to retry on
31-
* ``retry_on_timeout``: if ``True``, retry on :class:`~.TimeoutError` only
28+
* The :class:`~.Retry` instance has default set of :ref:`exceptions-label` to retry on,
29+
which can be overridden by passing a tuple with :ref:`exceptions-label` to the ``supported_errors`` parameter.
30+
* ``retry_on_error``: list of additional :ref:`exceptions-label` to retry on
3231

33-
If either ``retry_on_error`` or ``retry_on_timeout`` are passed and no ``retry`` is given,
34-
by default it uses a ``Retry(NoBackoff(), 1)`` (meaning 1 retry right after the first failure).
32+
33+
If no ``retry`` is provided, a default one is created with :class:`~.ExponentialWithJitterBackoff` as backoff strategy
34+
and 3 retries.
3535

3636

3737
Retry in Redis Cluster
@@ -44,27 +44,36 @@ Retry in Redis Cluster
4444
>>> # Run 3 retries with exponential backoff strategy
4545
>>> retry = Retry(ExponentialBackoff(), 3)
4646
>>> # Redis Cluster client with retries
47-
>>> rc = RedisCluster(host='localhost', port=6379, retry=retry, cluster_error_retry_attempts=2)
47+
>>> rc = RedisCluster(host='localhost', port=6379, retry=retry)
4848

4949
Retry behaviour in Redis Cluster is a little bit different from Standalone:
5050

51-
* ``retry``: :class:`~.Retry` instance with a :ref:`backoff-label` strategy and the max number of retries, default value is ``Retry(NoBackoff(), 0)``
52-
* ``cluster_error_retry_attempts``: number of times to retry before raising an error when :class:`~.TimeoutError` or :class:`~.ConnectionError` or :class:`~.ClusterDownError` are encountered, default value is ``3``
51+
* ``retry``: :class:`~.Retry` instance with a :ref:`backoff-label` strategy and the max number of retries, default value is ``Retry(ExponentialWithJitterBackoff(base=1, cap=10), cluster_error_retry_attempts)``
52+
* ``cluster_error_retry_attempts``: number of times to retry before raising an error when :class:`~.TimeoutError` or :class:`~.ConnectionError` or :class:`~.ClusterDownError` or :class:`~.SlotNotCoveredError` are encountered, default value is ``3``
53+
* This argument is deprecated - it is used to initialize the number of retries for the retry object,
54+
only in the case when the ``retry`` object is not provided.
55+
When the ``retry`` argument is provided, the ``cluster_error_retry_attempts`` argument is ignored!
56+
57+
* Starting from version 6.0.0 of the library, the default retry policy for the nodes connections is without retries.
58+
This means that if a connection to a node fails, the lower level connection will not retry the connection.
59+
Instead, it will raise a :class:`~.ConnectionError` to the cluster level call., where it will be retried.
60+
This is done to avoid blocking the cluster client for too long in case of a node failure.
61+
62+
* The retry object is not yet fully utilized in the cluster client.
63+
The retry object is used only to determine the number of retries for the cluster level calls.
5364

5465
Let's consider the following example:
5566

5667
>>> from redis.backoff import ExponentialBackoff
5768
>>> from redis.retry import Retry
5869
>>> from redis.cluster import RedisCluster
5970
>>>
60-
>>> rc = RedisCluster(host='localhost', port=6379, retry=Retry(ExponentialBackoff(), 6), cluster_error_retry_attempts=1)
71+
>>> rc = RedisCluster(host='localhost', port=6379, retry=Retry(ExponentialBackoff(), 6))
6172
>>> rc.set('foo', 'bar')
6273

6374
#. the client library calculates the hash slot for key 'foo'.
6475
#. given the hash slot, it then determines which node to connect to, in order to execute the command.
6576
#. during the connection, a :class:`~.ConnectionError` is raised.
66-
#. because we set ``retry=Retry(ExponentialBackoff(), 6)``, the client tries to reconnect to the node up to 6 times, with an exponential backoff between each attempt.
67-
#. even after 6 retries, the client is still unable to connect.
68-
#. because we set ``cluster_error_retry_attempts=1``, before giving up, the client starts a cluster update, removes the failed node from the startup nodes, and re-initializes the cluster.
69-
#. after the cluster has been re-initialized, it starts a new cycle of retries, up to 6 retries, with an exponential backoff.
70-
#. if the client can connect, we're good. Otherwise, the exception is finally raised to the caller, because we've run out of attempts.
77+
#. because the default retry policy for the nodes connections is without retries, the error is raised to the cluster level call
78+
#. because we set ``retry=Retry(ExponentialBackoff(), 6)``, the cluster client starts a cluster update, removes the failed node from the startup nodes, and re-initializes the cluster.
79+
#. the cluster client retries the command until it either succeeds or the max number of retries is reached.

redis/asyncio/cluster.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,9 @@ def get_retry(self) -> Retry:
570570

571571
def set_retry(self, retry: Retry) -> None:
572572
if not isinstance(retry, Retry):
573-
raise TypeError("retry must be a valid instance of redis.retry.Retry")
573+
raise TypeError(
574+
"retry must be a valid instance of redis.asyncio.retry.Retry"
575+
)
574576
self.retry = retry
575577

576578
def set_response_callback(self, command: str, callback: ResponseCallbackT) -> None:

0 commit comments

Comments
 (0)