Skip to content
This repository was archived by the owner on Jan 9, 2024. It is now read-only.

Commit 76aa25f

Browse files
committed
Create a new more specific error for the cases where a slot is not covered by the cluster. If this happens it will attempt to rebuild the cluster until TTL is expired and then raise that exception back to the user. Fixes #350
1 parent bf01104 commit 76aa25f

File tree

6 files changed

+36
-3
lines changed

6 files changed

+36
-3
lines changed

docs/release-notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Release Notes
2727
* Nodemanager initiailize should now handle usernames properly (#365)
2828
* PubSub tests has been all been disabled
2929
* New feature, host_port_remap. Send in a remapping configuration to RedisCluster instance where the nodes configuration recieved from the redis cluster can be altered to allow for connection in certain circumstances. See new section in clients.rst in docs/ for usage example.
30+
* When a slot is not covered by the cluster, it will not raise SlotNotCoveredError instead of the old generic RedisClusterException. The client will not attempt to rebuild the cluster layout a few times before giving up and raising that exception to the user. (#350)
3031

3132

3233
2.0.0 (Aug 12, 2019)

docs/upgrading.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ This document describes what must be done when upgrading between different versi
88

99
Python3 version must now be one of 3.5, 3.6, 3.7, 3.8
1010

11+
The following exception example has now a new more specific exception class that will be attempted to be caught and the client to resolve the cluster layout. If enough attempts has been made then SlotNotCoveredError will be raised with the same message as before. If you have catch for RedisClusterException you either remove it and let the client try to resolve the cluster layout itself, or start to catch SlotNotCoveredError. This error usually happens during failover if you run skip_full_coverage_check=True when running on AWS ElasticCache for example.
12+
13+
## Example exception
14+
rediscluster.exceptions.RedisClusterException: Slot "6986" not covered by the cluster. "skip_full_coverage_check=True"
15+
1116

1217
1.3.x --> 2.0.0
1318
---------------

rediscluster/client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
ClusterError,
2222
MovedError,
2323
RedisClusterException,
24+
SlotNotCoveredError,
2425
TryAgainError,
2526
)
2627
from .pubsub import ClusterPubSub
@@ -595,6 +596,16 @@ def _execute_command(self, *args, **kwargs):
595596

596597
connection.send_command(*args)
597598
return self.parse_response(connection, command, **kwargs)
599+
except SlotNotCoveredError as e:
600+
# In some cases during failover to a replica is happening
601+
# a slot sometimes is not covered by the cluster layout and
602+
# we need to attempt to refresh the cluster layout and try again
603+
self.refresh_table_asap = True
604+
time.sleep(0.05)
605+
606+
# This is the last attempt before we run out of TTL, raise the exception
607+
if ttl == 1:
608+
raise e
598609
except (RedisClusterException, BusyLoadingError):
599610
raise
600611
except ConnectionError:

rediscluster/connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from .exceptions import (
1515
RedisClusterException, AskError, MovedError,
1616
TryAgainError, ClusterDownError, ClusterCrossSlotError,
17-
MasterDownError,
17+
MasterDownError, SlotNotCoveredError,
1818
)
1919

2020
# 3rd party imports
@@ -329,7 +329,7 @@ def get_master_node_by_slot(self, slot):
329329
try:
330330
return self.nodes.slots[slot][0]
331331
except KeyError as ke:
332-
raise RedisClusterException('Slot "{slot}" not covered by the cluster. "skip_full_coverage_check={skip_full_coverage_check}"'.format(
332+
raise SlotNotCoveredError('Slot "{slot}" not covered by the cluster. "skip_full_coverage_check={skip_full_coverage_check}"'.format(
333333
slot=slot, skip_full_coverage_check=self.nodes._skip_full_coverage_check,
334334
))
335335

rediscluster/exceptions.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,14 @@ class MasterDownError(ClusterDownError):
8585
"""
8686
"""
8787
pass
88+
89+
90+
class SlotNotCoveredError(RedisClusterException):
91+
"""
92+
This error only happens in the case where the connection pool will try to
93+
fetch what node that is covered by a given slot.
94+
95+
If this error is raised the client should drop the current node layout and
96+
attempt to reconnect and refresh the node layout again
97+
"""
98+
pass

tests/test_cluster_connection_pool.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from rediscluster.connection import (
1212
ClusterConnectionPool, ClusterBlockingConnectionPool, ClusterReadOnlyConnectionPool,
1313
ClusterConnection, UnixDomainSocketConnection)
14-
from rediscluster.exceptions import RedisClusterException
14+
from rediscluster.exceptions import RedisClusterException, SlotNotCoveredError
1515
from .conftest import (skip_if_server_version_lt, skip_for_no_cluster_impl)
1616

1717
# 3rd party imports
@@ -205,6 +205,11 @@ def test_master_node_by_slot(self):
205205
node = pool.get_master_node_by_slot(12182)
206206
node['port'] = 7002
207207

208+
pool = self.get_pool(connection_kwargs={})
209+
pool.nodes.slots = {}
210+
with pytest.raises(SlotNotCoveredError):
211+
pool.get_master_node_by_slot(12182)
212+
208213
def test_from_url_connection_classes(self):
209214
from rediscluster.client import RedisCluster
210215
from rediscluster.connection import ClusterConnectionPool, ClusterConnection, SSLClusterConnection

0 commit comments

Comments
 (0)