Skip to content

Commit 2bb7f10

Browse files
bmacpheechayimuglidedvora-h
authored
Introduce OutOfMemoryError exception for Redis write command rejections due to OOM errors (#2778)
* expose OutOfMemoryError as explicit exception type - handle "OOM" error code string by raising explicit exception type instance - enables callers to avoid string matching after catching ResponseError * add OutOfMemoryError exception class docstring * Provide more info in the exception docstring * Fix formatting * Again * linters --------- Co-authored-by: Chayim <[email protected]> Co-authored-by: Igor Malinovskiy <[email protected]> Co-authored-by: dvora-h <[email protected]>
1 parent 29dfbb2 commit 2bb7f10

File tree

7 files changed

+37
-2
lines changed

7 files changed

+37
-2
lines changed

redis/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
ConnectionError,
2020
DataError,
2121
InvalidResponse,
22+
OutOfMemoryError,
2223
PubSubError,
2324
ReadOnlyError,
2425
RedisError,
@@ -72,6 +73,7 @@ def int_or_str(value):
7273
"from_url",
7374
"default_backoff",
7475
"InvalidResponse",
76+
"OutOfMemoryError",
7577
"PubSubError",
7678
"ReadOnlyError",
7779
"Redis",

redis/asyncio/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
ConnectionError,
2525
DataError,
2626
InvalidResponse,
27+
OutOfMemoryError,
2728
PubSubError,
2829
ReadOnlyError,
2930
RedisError,
@@ -47,6 +48,7 @@
4748
"default_backoff",
4849
"InvalidResponse",
4950
"PubSubError",
51+
"OutOfMemoryError",
5052
"ReadOnlyError",
5153
"Redis",
5254
"RedisCluster",

redis/asyncio/connection.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
ModuleError,
5050
NoPermissionError,
5151
NoScriptError,
52+
OutOfMemoryError,
5253
ReadOnlyError,
5354
RedisError,
5455
ResponseError,
@@ -174,6 +175,7 @@ class BaseParser:
174175
"READONLY": ReadOnlyError,
175176
"NOAUTH": AuthenticationError,
176177
"NOPERM": NoPermissionError,
178+
"OOM": OutOfMemoryError,
177179
}
178180

179181
def __init__(self, socket_read_size: int):

redis/connection.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
ModuleError,
2929
NoPermissionError,
3030
NoScriptError,
31+
OutOfMemoryError,
3132
ReadOnlyError,
3233
RedisError,
3334
ResponseError,
@@ -149,6 +150,7 @@ class BaseParser:
149150
MODULE_UNLOAD_NOT_POSSIBLE_ERROR: ModuleError,
150151
**NO_AUTH_SET_ERROR,
151152
},
153+
"OOM": OutOfMemoryError,
152154
"WRONGPASS": AuthenticationError,
153155
"EXECABORT": ExecAbortError,
154156
"LOADING": BusyLoadingError,

redis/exceptions.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ class NoScriptError(ResponseError):
4949
pass
5050

5151

52+
class OutOfMemoryError(ResponseError):
53+
"""
54+
Indicates the database is full. Can only occur when either:
55+
* Redis maxmemory-policy=noeviction
56+
* Redis maxmemory-policy=volatile* and there are no evictable keys
57+
58+
For more information see `Memory optimization in Redis <https://redis.io/docs/management/optimization/memory-optimization/#memory-allocation>`_. # noqa
59+
"""
60+
61+
pass
62+
63+
5264
class ExecAbortError(ResponseError):
5365
pass
5466

tests/test_asyncio/test_connection_pool.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,10 +606,18 @@ async def test_busy_loading_from_pipeline(self, r):
606606
@skip_if_server_version_lt("2.8.8")
607607
@skip_if_redis_enterprise()
608608
async def test_read_only_error(self, r):
609-
"""READONLY errors get turned in ReadOnlyError exceptions"""
609+
"""READONLY errors get turned into ReadOnlyError exceptions"""
610610
with pytest.raises(redis.ReadOnlyError):
611611
await r.execute_command("DEBUG", "ERROR", "READONLY blah blah")
612612

613+
@skip_if_redis_enterprise()
614+
async def test_oom_error(self, r):
615+
"""OOM errors get turned into OutOfMemoryError exceptions"""
616+
with pytest.raises(redis.OutOfMemoryError):
617+
# note: don't use the DEBUG OOM command since it's not the same
618+
# as the db being full
619+
await r.execute_command("DEBUG", "ERROR", "OOM blah blah")
620+
613621
def test_connect_from_url_tcp(self):
614622
connection = redis.Redis.from_url("redis://localhost")
615623
pool = connection.connection_pool

tests/test_connection_pool.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,10 +528,17 @@ def test_busy_loading_from_pipeline(self, r):
528528
@skip_if_server_version_lt("2.8.8")
529529
@skip_if_redis_enterprise()
530530
def test_read_only_error(self, r):
531-
"READONLY errors get turned in ReadOnlyError exceptions"
531+
"READONLY errors get turned into ReadOnlyError exceptions"
532532
with pytest.raises(redis.ReadOnlyError):
533533
r.execute_command("DEBUG", "ERROR", "READONLY blah blah")
534534

535+
def test_oom_error(self, r):
536+
"OOM errors get turned into OutOfMemoryError exceptions"
537+
with pytest.raises(redis.OutOfMemoryError):
538+
# note: don't use the DEBUG OOM command since it's not the same
539+
# as the db being full
540+
r.execute_command("DEBUG", "ERROR", "OOM blah blah")
541+
535542
def test_connect_from_url_tcp(self):
536543
connection = redis.Redis.from_url("redis://localhost")
537544
pool = connection.connection_pool

0 commit comments

Comments
 (0)