Skip to content

Commit c8010af

Browse files
committed
Fixed bug with disabled auto_fallback_interval
1 parent ad4483e commit c8010af

File tree

4 files changed

+110
-3
lines changed

4 files changed

+110
-3
lines changed

redis/asyncio/multidb/command_executor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ async def _check_active_database(self):
303303
self._active_database is None
304304
or self._active_database.circuit.state != CBState.CLOSED
305305
or (
306-
self._auto_fallback_interval != DEFAULT_AUTO_FALLBACK_INTERVAL
306+
self._auto_fallback_interval > 0
307307
and self._next_fallback_attempt <= datetime.now()
308308
)
309309
):

redis/multidb/command_executor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def auto_fallback_interval(self, auto_fallback_interval: int) -> None:
5555
self._auto_fallback_interval = auto_fallback_interval
5656

5757
def _schedule_next_fallback(self) -> None:
58-
if self._auto_fallback_interval == DEFAULT_AUTO_FALLBACK_INTERVAL:
58+
if self._auto_fallback_interval < 0:
5959
return
6060

6161
self._next_fallback_attempt = datetime.now() + timedelta(
@@ -321,7 +321,7 @@ def _check_active_database(self):
321321
self._active_database is None
322322
or self._active_database.circuit.state != CBState.CLOSED
323323
or (
324-
self._auto_fallback_interval != DEFAULT_AUTO_FALLBACK_INTERVAL
324+
self._auto_fallback_interval > 0
325325
and self._next_fallback_attempt <= datetime.now()
326326
)
327327
):

tests/test_asyncio/test_multidb/test_client.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,60 @@ async def mock_check_health(database):
275275
await asyncio.sleep(0.5)
276276
assert await client.set("key", "value") == "OK1"
277277

278+
@pytest.mark.asyncio
279+
@pytest.mark.parametrize(
280+
"mock_multi_db_config,mock_db, mock_db1, mock_db2",
281+
[
282+
(
283+
{"health_check_probes": 1},
284+
{"weight": 0.2, "circuit": {"state": CBState.CLOSED}},
285+
{"weight": 0.7, "circuit": {"state": CBState.CLOSED}},
286+
{"weight": 0.5, "circuit": {"state": CBState.CLOSED}},
287+
),
288+
],
289+
indirect=True,
290+
)
291+
async def test_execute_command_do_not_auto_fallback_to_highest_weight_db(
292+
self, mock_multi_db_config, mock_db, mock_db1, mock_db2, mock_hc
293+
):
294+
databases = create_weighted_list(mock_db, mock_db1, mock_db2)
295+
db1_counter = 0
296+
error_event = asyncio.Event()
297+
check = False
298+
299+
async def mock_check_health(database):
300+
nonlocal db1_counter, check
301+
302+
if database == mock_db1 and not check:
303+
db1_counter += 1
304+
305+
if db1_counter > 1:
306+
error_event.set()
307+
check = True
308+
return False
309+
310+
return True
311+
312+
mock_hc.check_health.side_effect = mock_check_health
313+
mock_multi_db_config.health_checks = [mock_hc]
314+
315+
with (
316+
patch.object(mock_multi_db_config, "databases", return_value=databases),
317+
):
318+
mock_db.client.execute_command.return_value = "OK"
319+
mock_db1.client.execute_command.return_value = "OK1"
320+
mock_db2.client.execute_command.return_value = "OK2"
321+
mock_multi_db_config.health_check_interval = 0.1
322+
mock_multi_db_config.auto_fallback_interval = -1
323+
mock_multi_db_config.failover_strategy = WeightBasedFailoverStrategy()
324+
325+
async with MultiDBClient(mock_multi_db_config) as client:
326+
assert await client.set("key", "value") == "OK1"
327+
await error_event.wait()
328+
assert await client.set("key", "value") == "OK2"
329+
await asyncio.sleep(0.5)
330+
assert await client.set("key", "value") == "OK2"
331+
278332
@pytest.mark.asyncio
279333
@pytest.mark.parametrize(
280334
"mock_multi_db_config,mock_db, mock_db1, mock_db2",

tests/test_multidb/test_client.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,59 @@ def mock_check_health(database):
272272
sleep(0.5)
273273
assert client.set("key", "value") == "OK1"
274274

275+
@pytest.mark.parametrize(
276+
"mock_multi_db_config,mock_db, mock_db1, mock_db2",
277+
[
278+
(
279+
{"health_check_probes": 1},
280+
{"weight": 0.2, "circuit": {"state": CBState.CLOSED}},
281+
{"weight": 0.7, "circuit": {"state": CBState.CLOSED}},
282+
{"weight": 0.5, "circuit": {"state": CBState.CLOSED}},
283+
),
284+
],
285+
indirect=True,
286+
)
287+
def test_execute_command_do_not_auto_fallback_to_highest_weight_db(
288+
self, mock_multi_db_config, mock_db, mock_db1, mock_db2, mock_hc
289+
):
290+
databases = create_weighted_list(mock_db, mock_db1, mock_db2)
291+
db1_counter = 0
292+
error_event = threading.Event()
293+
check = False
294+
295+
def mock_check_health(database):
296+
nonlocal db1_counter, check
297+
298+
if database == mock_db1 and not check:
299+
db1_counter += 1
300+
301+
if db1_counter > 1:
302+
error_event.set()
303+
check = True
304+
return False
305+
306+
return True
307+
308+
mock_hc.check_health.side_effect = mock_check_health
309+
mock_multi_db_config.health_checks = [mock_hc]
310+
311+
with (
312+
patch.object(mock_multi_db_config, "databases", return_value=databases),
313+
):
314+
mock_db.client.execute_command.return_value = "OK"
315+
mock_db1.client.execute_command.return_value = "OK1"
316+
mock_db2.client.execute_command.return_value = "OK2"
317+
mock_multi_db_config.health_check_interval = 0.1
318+
mock_multi_db_config.auto_fallback_interval = -1
319+
mock_multi_db_config.failover_strategy = WeightBasedFailoverStrategy()
320+
321+
client = MultiDBClient(mock_multi_db_config)
322+
assert client.set("key", "value") == "OK1"
323+
error_event.wait(timeout=0.5)
324+
assert client.set("key", "value") == "OK2"
325+
sleep(0.5)
326+
assert client.set("key", "value") == "OK2"
327+
275328
@pytest.mark.parametrize(
276329
"mock_multi_db_config,mock_db, mock_db1, mock_db2",
277330
[

0 commit comments

Comments
 (0)