Skip to content

Commit 64a2721

Browse files
vladvildanovpetyaslavova
authored andcommitted
Refactor healthcheck to use PING instead of ECHO (#3811)
1 parent 24a7a10 commit 64a2721

File tree

11 files changed

+56
-46
lines changed

11 files changed

+56
-46
lines changed

redis/asyncio/multidb/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
from redis.multidb.circuit import State as CBState
1515
from redis.multidb.exception import NoValidDatabaseException, UnhealthyDatabaseException
1616
from redis.typing import ChannelT, EncodableT, KeyT
17+
from redis.utils import experimental
1718

1819
logger = logging.getLogger(__name__)
1920

2021

22+
@experimental
2123
class MultiDBClient(AsyncRedisModuleCommands, AsyncCoreCommands):
2224
"""
2325
Client that operates on multiple logical Redis databases.

redis/asyncio/multidb/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
DEFAULT_HEALTH_CHECK_INTERVAL,
2121
DEFAULT_HEALTH_CHECK_POLICY,
2222
DEFAULT_HEALTH_CHECK_PROBES,
23-
EchoHealthCheck,
2423
HealthCheck,
2524
HealthCheckPolicies,
25+
PingHealthCheck,
2626
)
2727
from redis.asyncio.retry import Retry
2828
from redis.backoff import ExponentialWithJitterBackoff, NoBackoff
@@ -203,7 +203,7 @@ def default_failure_detectors(self) -> List[AsyncFailureDetector]:
203203

204204
def default_health_checks(self) -> List[HealthCheck]:
205205
return [
206-
EchoHealthCheck(),
206+
PingHealthCheck(),
207207
]
208208

209209
def default_failover_strategy(self) -> AsyncFailoverStrategy:

redis/asyncio/multidb/healthcheck.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -170,26 +170,19 @@ class HealthCheckPolicies(Enum):
170170
DEFAULT_HEALTH_CHECK_POLICY: HealthCheckPolicies = HealthCheckPolicies.HEALTHY_ALL
171171

172172

173-
class EchoHealthCheck(HealthCheck):
173+
class PingHealthCheck(HealthCheck):
174174
"""
175-
Health check based on ECHO command.
175+
Health check based on PING command.
176176
"""
177177

178178
async def check_health(self, database) -> bool:
179-
expected_message = ["healthcheck", b"healthcheck"]
180-
181179
if isinstance(database.client, Redis):
182-
actual_message = await database.client.execute_command(
183-
"ECHO", "healthcheck"
184-
)
185-
return actual_message in expected_message
180+
return await database.client.execute_command("PING")
186181
else:
187182
# For a cluster checks if all nodes are healthy.
188183
all_nodes = database.client.get_nodes()
189184
for node in all_nodes:
190-
actual_message = await node.execute_command("ECHO", "healthcheck")
191-
192-
if actual_message not in expected_message:
185+
if not await node.redis_connection.execute_command("PING"):
193186
return False
194187

195188
return True

redis/multidb/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
from redis.multidb.exception import NoValidDatabaseException, UnhealthyDatabaseException
1616
from redis.multidb.failure_detector import FailureDetector
1717
from redis.multidb.healthcheck import HealthCheck, HealthCheckPolicy
18+
from redis.utils import experimental
1819

1920
logger = logging.getLogger(__name__)
2021

2122

23+
@experimental
2224
class MultiDBClient(RedisModuleCommands, CoreCommands):
2325
"""
2426
Client that operates on multiple logical Redis databases.

redis/multidb/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
DEFAULT_HEALTH_CHECK_INTERVAL,
3333
DEFAULT_HEALTH_CHECK_POLICY,
3434
DEFAULT_HEALTH_CHECK_PROBES,
35-
EchoHealthCheck,
3635
HealthCheck,
3736
HealthCheckPolicies,
37+
PingHealthCheck,
3838
)
3939
from redis.retry import Retry
4040

@@ -200,7 +200,7 @@ def default_failure_detectors(self) -> List[FailureDetector]:
200200

201201
def default_health_checks(self) -> List[HealthCheck]:
202202
return [
203-
EchoHealthCheck(),
203+
PingHealthCheck(),
204204
]
205205

206206
def default_failover_strategy(self) -> FailoverStrategy:

redis/multidb/healthcheck.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,26 +169,19 @@ class HealthCheckPolicies(Enum):
169169
DEFAULT_HEALTH_CHECK_POLICY: HealthCheckPolicies = HealthCheckPolicies.HEALTHY_ALL
170170

171171

172-
class EchoHealthCheck(HealthCheck):
172+
class PingHealthCheck(HealthCheck):
173173
"""
174-
Health check based on ECHO command.
174+
Health check based on PING command.
175175
"""
176176

177177
def check_health(self, database) -> bool:
178-
expected_message = ["healthcheck", b"healthcheck"]
179-
180178
if isinstance(database.client, Redis):
181-
actual_message = database.client.execute_command("ECHO", "healthcheck")
182-
return actual_message in expected_message
179+
return database.client.execute_command("PING")
183180
else:
184181
# For a cluster checks if all nodes are healthy.
185182
all_nodes = database.client.get_nodes()
186183
for node in all_nodes:
187-
actual_message = node.redis_connection.execute_command(
188-
"ECHO", "healthcheck"
189-
)
190-
191-
if actual_message not in expected_message:
184+
if not node.redis_connection.execute_command("PING"):
192185
return False
193186

194187
return True

redis/utils.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import datetime
22
import logging
33
import textwrap
4+
import warnings
45
from collections.abc import Callable
56
from contextlib import contextmanager
67
from functools import wraps
@@ -326,3 +327,22 @@ async def dummy_fail_async():
326327
Async fake function for a Retry object if you don't need to handle each failure.
327328
"""
328329
pass
330+
331+
332+
def experimental(cls):
333+
"""
334+
Decorator to mark a class as experimental.
335+
"""
336+
original_init = cls.__init__
337+
338+
@wraps(original_init)
339+
def new_init(self, *args, **kwargs):
340+
warnings.warn(
341+
f"{cls.__name__} is an experimental and may change or be removed in future versions.",
342+
category=UserWarning,
343+
stacklevel=2,
344+
)
345+
original_init(self, *args, **kwargs)
346+
347+
cls.__init__ = new_init
348+
return cls

tests/test_asyncio/test_multidb/test_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
FailureDetectorAsyncWrapper,
2020
AsyncFailureDetector,
2121
)
22-
from redis.asyncio.multidb.healthcheck import EchoHealthCheck, HealthCheck
22+
from redis.asyncio.multidb.healthcheck import PingHealthCheck, HealthCheck
2323
from redis.asyncio.retry import Retry
2424
from redis.multidb.circuit import CircuitBreaker
2525

@@ -58,7 +58,7 @@ def test_default_config(self):
5858
config.default_failure_detectors()[0], FailureDetectorAsyncWrapper
5959
)
6060
assert len(config.default_health_checks()) == 1
61-
assert isinstance(config.default_health_checks()[0], EchoHealthCheck)
61+
assert isinstance(config.default_health_checks()[0], PingHealthCheck)
6262
assert config.health_check_interval == DEFAULT_HEALTH_CHECK_INTERVAL
6363
assert isinstance(
6464
config.default_failover_strategy(), WeightBasedFailoverStrategy

tests/test_asyncio/test_multidb/test_healthcheck.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from redis.asyncio.multidb.database import Database
55
from redis.asyncio.multidb.healthcheck import (
6-
EchoHealthCheck,
6+
PingHealthCheck,
77
LagAwareHealthCheck,
88
HealthCheck,
99
HealthyAllPolicy,
@@ -208,15 +208,15 @@ async def test_policy_raise_unhealthy_database_exception_if_exception_occurs_on_
208208

209209

210210
@pytest.mark.onlynoncluster
211-
class TestEchoHealthCheck:
211+
class TestPingHealthCheck:
212212
@pytest.mark.asyncio
213213
async def test_database_is_healthy_on_echo_response(self, mock_client, mock_cb):
214214
"""
215215
Mocking responses to mix error and actual responses to ensure that health check retry
216216
according to given configuration.
217217
"""
218-
mock_client.execute_command = AsyncMock(side_effect=["healthcheck"])
219-
hc = EchoHealthCheck()
218+
mock_client.execute_command = AsyncMock(side_effect=["PONG"])
219+
hc = PingHealthCheck()
220220
db = Database(mock_client, mock_cb, 0.9)
221221

222222
assert await hc.check_health(db)
@@ -230,8 +230,8 @@ async def test_database_is_unhealthy_on_incorrect_echo_response(
230230
Mocking responses to mix error and actual responses to ensure that health check retry
231231
according to given configuration.
232232
"""
233-
mock_client.execute_command = AsyncMock(side_effect=["wrong"])
234-
hc = EchoHealthCheck()
233+
mock_client.execute_command = AsyncMock(side_effect=[False])
234+
hc = PingHealthCheck()
235235
db = Database(mock_client, mock_cb, 0.9)
236236

237237
assert not await hc.check_health(db)
@@ -241,9 +241,9 @@ async def test_database_is_unhealthy_on_incorrect_echo_response(
241241
async def test_database_close_circuit_on_successful_healthcheck(
242242
self, mock_client, mock_cb
243243
):
244-
mock_client.execute_command = AsyncMock(side_effect=["healthcheck"])
244+
mock_client.execute_command = AsyncMock(side_effect=["PONG"])
245245
mock_cb.state = CBState.HALF_OPEN
246-
hc = EchoHealthCheck()
246+
hc = PingHealthCheck()
247247
db = Database(mock_client, mock_cb, 0.9)
248248

249249
assert await hc.check_health(db)

tests/test_multidb/test_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
)
1717
from redis.multidb.database import Database
1818
from redis.multidb.failure_detector import CommandFailureDetector, FailureDetector
19-
from redis.multidb.healthcheck import EchoHealthCheck, HealthCheck
19+
from redis.multidb.healthcheck import PingHealthCheck, HealthCheck
2020
from redis.multidb.failover import WeightBasedFailoverStrategy, FailoverStrategy
2121
from redis.retry import Retry
2222

@@ -53,7 +53,7 @@ def test_default_config(self):
5353
assert len(config.default_failure_detectors()) == 1
5454
assert isinstance(config.default_failure_detectors()[0], CommandFailureDetector)
5555
assert len(config.default_health_checks()) == 1
56-
assert isinstance(config.default_health_checks()[0], EchoHealthCheck)
56+
assert isinstance(config.default_health_checks()[0], PingHealthCheck)
5757
assert config.health_check_interval == DEFAULT_HEALTH_CHECK_INTERVAL
5858
assert isinstance(
5959
config.default_failover_strategy(), WeightBasedFailoverStrategy

0 commit comments

Comments
 (0)