redis-client-kit 0.1.5__tar.gz → 0.2.0__tar.gz
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_client_kit-0.1.5 → redis_client_kit-0.2.0}/CHANGELOG.md +7 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/PKG-INFO +6 -1
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/README.md +5 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/__init__.py +1 -1
- redis_client_kit-0.2.0/redis_client_kit/__version__.py +1 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/aio/lifecycle.py +17 -3
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/sync/lifecycle.py +17 -3
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/utils.py +3 -0
- redis_client_kit-0.1.5/redis_client_kit/__version__.py +0 -1
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/.gitignore +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/LICENSE +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/pyproject.toml +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/aio/__init__.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/aio/factory.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/aio/instrumented.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/aio/types.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/config.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/metrics/__init__.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/metrics/_deps.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/metrics/redis.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/protocols.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/providers/__init__.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/providers/_deps.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/providers/redis.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/providers/utils.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/py.typed +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/settings/__init__.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/settings/_deps.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/settings/redis.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/sync/__init__.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/sync/factory.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/sync/instrumented.py +0 -0
- {redis_client_kit-0.1.5 → redis_client_kit-0.2.0}/redis_client_kit/sync/types.py +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.0](https://github.com/bedrock-python/redis-client-kit/compare/redis-client-kit-v0.1.5...redis-client-kit-v0.2.0) (2026-09-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add an opt-in write probe to the health checks ([#32](https://github.com/bedrock-python/redis-client-kit/issues/32)) ([aabafd1](https://github.com/bedrock-python/redis-client-kit/commit/aabafd1922e3e0cf1b525570f98cdf0f71a2856e))
|
|
9
|
+
|
|
3
10
|
## [0.1.5](https://github.com/bedrock-python/redis-client-kit/compare/redis-client-kit-v0.1.4...redis-client-kit-v0.1.5) (2026-09-07)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: redis-client-kit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Redis client with optional Pydantic, Prometheus, and Dishka support
|
|
5
5
|
Project-URL: Repository, https://github.com/bedrock-python/redis-client-kit
|
|
6
6
|
Project-URL: Documentation, https://bedrock-python.github.io/redis-client-kit/
|
|
@@ -491,6 +491,11 @@ from redis_client_kit import check_async_redis_health
|
|
|
491
491
|
is_healthy = await check_async_redis_health(client)
|
|
492
492
|
if is_healthy:
|
|
493
493
|
print("Redis is ready!")
|
|
494
|
+
|
|
495
|
+
# PING alone says healthy for a read-only replica and for a primary that is out of memory
|
|
496
|
+
# under noeviction. write_key adds a SET <key> 1 EX 60 after the ping, for a service that
|
|
497
|
+
# needs Redis for more than reads.
|
|
498
|
+
is_healthy = await check_async_redis_health(client, write_key="myapp:health")
|
|
494
499
|
```
|
|
495
500
|
|
|
496
501
|
### Graceful Shutdown
|
|
@@ -251,6 +251,11 @@ from redis_client_kit import check_async_redis_health
|
|
|
251
251
|
is_healthy = await check_async_redis_health(client)
|
|
252
252
|
if is_healthy:
|
|
253
253
|
print("Redis is ready!")
|
|
254
|
+
|
|
255
|
+
# PING alone says healthy for a read-only replica and for a primary that is out of memory
|
|
256
|
+
# under noeviction. write_key adds a SET <key> 1 EX 60 after the ping, for a service that
|
|
257
|
+
# needs Redis for more than reads.
|
|
258
|
+
is_healthy = await check_async_redis_health(client, write_key="myapp:health")
|
|
254
259
|
```
|
|
255
260
|
|
|
256
261
|
### Graceful Shutdown
|
|
@@ -24,7 +24,7 @@ from .utils import build_base_redis_kwargs, build_redis_retry, parse_redis_url_n
|
|
|
24
24
|
try:
|
|
25
25
|
__version__ = version("redis-client-kit")
|
|
26
26
|
except PackageNotFoundError: # pragma: no cover
|
|
27
|
-
__version__ = "0.
|
|
27
|
+
__version__ = "0.2.0"
|
|
28
28
|
|
|
29
29
|
# ruff: noqa: RUF022
|
|
30
30
|
__all__ = [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -8,6 +8,7 @@ from typing import Any
|
|
|
8
8
|
|
|
9
9
|
from redis.exceptions import RedisClusterException, RedisError
|
|
10
10
|
|
|
11
|
+
from ..utils import WRITE_PROBE_TTL_S
|
|
11
12
|
from .types import AsyncRedisClient
|
|
12
13
|
|
|
13
14
|
logger = logging.getLogger(__name__)
|
|
@@ -36,11 +37,19 @@ async def close_async_redis_client(client: AsyncRedisClient) -> None:
|
|
|
36
37
|
logger.info("Closed Redis client")
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
async def check_async_redis_health(client: AsyncRedisClient) -> bool:
|
|
40
|
+
async def check_async_redis_health(client: AsyncRedisClient, *, write_key: str | None = None) -> bool:
|
|
40
41
|
"""Check async Redis connection health.
|
|
41
42
|
|
|
43
|
+
Pings the server, and with ``write_key`` writes that key as well. A ping alone is a
|
|
44
|
+
liveness answer: a read-only replica and a server at ``maxmemory`` under ``noeviction``
|
|
45
|
+
both reply PONG and refuse every write. The write probe turns that into a readiness
|
|
46
|
+
answer for a service that needs Redis for more than reads.
|
|
47
|
+
|
|
42
48
|
Args:
|
|
43
49
|
client: Redis client (single or cluster) to check
|
|
50
|
+
write_key: Key to ``SET`` after the ping, expiring after ``WRITE_PROBE_TTL_S``
|
|
51
|
+
seconds. None, the default, pings only. Prefix it yourself; this package
|
|
52
|
+
applies no key prefix.
|
|
44
53
|
|
|
45
54
|
Returns:
|
|
46
55
|
True if Redis is healthy, False otherwise
|
|
@@ -50,9 +59,12 @@ async def check_async_redis_health(client: AsyncRedisClient) -> bool:
|
|
|
50
59
|
|
|
51
60
|
# Redis Cluster ping returns dict[str, bool] (node_id -> success); single node returns bool.
|
|
52
61
|
if isinstance(result, dict) and result and all(isinstance(k, str) for k in result):
|
|
53
|
-
|
|
62
|
+
healthy = all(bool(v) for v in result.values())
|
|
63
|
+
else:
|
|
64
|
+
healthy = bool(result)
|
|
54
65
|
|
|
55
|
-
|
|
66
|
+
if healthy and write_key is not None:
|
|
67
|
+
healthy = bool(await client.set(write_key, "1", ex=WRITE_PROBE_TTL_S))
|
|
56
68
|
except (RedisError, RedisClusterException, OSError, ConnectionError, TimeoutError) as e:
|
|
57
69
|
logger.warning(
|
|
58
70
|
"Async Redis health check failed",
|
|
@@ -66,6 +78,8 @@ async def check_async_redis_health(client: AsyncRedisClient) -> bool:
|
|
|
66
78
|
# We return False to indicate unhealthy state but log the full exception for debugging.
|
|
67
79
|
logger.exception("Unexpected error during Redis health check")
|
|
68
80
|
return False
|
|
81
|
+
else:
|
|
82
|
+
return healthy
|
|
69
83
|
|
|
70
84
|
|
|
71
85
|
__all__ = ["check_async_redis_health", "close_async_redis_client"]
|
|
@@ -7,6 +7,7 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
from redis.exceptions import RedisClusterException, RedisError
|
|
9
9
|
|
|
10
|
+
from ..utils import WRITE_PROBE_TTL_S
|
|
10
11
|
from .types import SyncRedisClient
|
|
11
12
|
|
|
12
13
|
logger = logging.getLogger(__name__)
|
|
@@ -28,11 +29,19 @@ def close_redis_client(client: SyncRedisClient) -> None:
|
|
|
28
29
|
logger.info("Closed Redis client")
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
def check_redis_health(client: SyncRedisClient) -> bool:
|
|
32
|
+
def check_redis_health(client: SyncRedisClient, *, write_key: str | None = None) -> bool:
|
|
32
33
|
"""Check sync Redis connection health.
|
|
33
34
|
|
|
35
|
+
Pings the server, and with ``write_key`` writes that key as well. A ping alone is a
|
|
36
|
+
liveness answer: a read-only replica and a server at ``maxmemory`` under ``noeviction``
|
|
37
|
+
both reply PONG and refuse every write. The write probe turns that into a readiness
|
|
38
|
+
answer for a service that needs Redis for more than reads.
|
|
39
|
+
|
|
34
40
|
Args:
|
|
35
41
|
client: Redis client (single or cluster) to check
|
|
42
|
+
write_key: Key to ``SET`` after the ping, expiring after ``WRITE_PROBE_TTL_S``
|
|
43
|
+
seconds. None, the default, pings only. Prefix it yourself; this package
|
|
44
|
+
applies no key prefix.
|
|
36
45
|
|
|
37
46
|
Returns:
|
|
38
47
|
True if Redis is healthy, False otherwise
|
|
@@ -42,9 +51,12 @@ def check_redis_health(client: SyncRedisClient) -> bool:
|
|
|
42
51
|
|
|
43
52
|
# Redis Cluster ping returns dict[str, bool] (node_id -> success); single node returns bool.
|
|
44
53
|
if isinstance(result, dict) and result and all(isinstance(k, str) for k in result):
|
|
45
|
-
|
|
54
|
+
healthy = all(bool(v) for v in result.values())
|
|
55
|
+
else:
|
|
56
|
+
healthy = bool(result)
|
|
46
57
|
|
|
47
|
-
|
|
58
|
+
if healthy and write_key is not None:
|
|
59
|
+
healthy = bool(client.set(write_key, "1", ex=WRITE_PROBE_TTL_S))
|
|
48
60
|
except (RedisError, RedisClusterException, OSError, ConnectionError, TimeoutError) as e:
|
|
49
61
|
logger.warning(
|
|
50
62
|
"Redis health check failed",
|
|
@@ -58,6 +70,8 @@ def check_redis_health(client: SyncRedisClient) -> bool:
|
|
|
58
70
|
# We return False to indicate unhealthy state but log the full exception for debugging.
|
|
59
71
|
logger.exception("Unexpected error during Redis health check")
|
|
60
72
|
return False
|
|
73
|
+
else:
|
|
74
|
+
return healthy
|
|
61
75
|
|
|
62
76
|
|
|
63
77
|
__all__ = ["check_redis_health", "close_redis_client"]
|
|
@@ -12,6 +12,8 @@ from redis.retry import Retry
|
|
|
12
12
|
|
|
13
13
|
from .config import RedisSettingsProtocol
|
|
14
14
|
|
|
15
|
+
WRITE_PROBE_TTL_S = 60
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
def parse_redis_url_node(node: str) -> tuple[str, int]:
|
|
17
19
|
"""Parse Redis node from host:port, [ipv6]:port or redis:// URL string."""
|
|
@@ -143,6 +145,7 @@ def mask_redis_kwargs(kwargs: dict[str, object]) -> dict[str, object]:
|
|
|
143
145
|
|
|
144
146
|
|
|
145
147
|
__all__ = [
|
|
148
|
+
"WRITE_PROBE_TTL_S",
|
|
146
149
|
"build_base_redis_kwargs",
|
|
147
150
|
"build_redis_retry",
|
|
148
151
|
"mask_redis_kwargs",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.5"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|