Skip to content

Commit 88ae872

Browse files
fix: address CodeRabbit PR feedback on lifecycle management
- MongoDB: Remove duplicate client cleanup from __aexit__, update _close() to call __aexit__(None, None, None) for symmetry with __aenter__ - DynamoDB: Move client cleanup before super().__aexit__() to prevent double-cleanup, set _client = None after closing - RocksDB: Add try-except in __del__ for best-effort cleanup with appropriate noqa comments - DuckDB: Use getattr for _is_closed check in __del__ for consistency - Disk: Set _client_provided_by_user early for exception safety - Memcached: Change truthiness check to explicit 'is not None' for consistency - Valkey: Fix _close() to close the correct client (_connected_client) and set it to None for idempotency Co-authored-by: William Easton <[email protected]>
1 parent 8a62fe1 commit 88ae872

File tree

12 files changed

+33
-20
lines changed

12 files changed

+33
-20
lines changed

key-value/key-value-aio/src/key_value/aio/stores/disk/store.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def __init__(
6666
raise ValueError(msg)
6767

6868
client_provided = disk_cache is not None
69+
self._client_provided_by_user = client_provided
6970

7071
if disk_cache:
7172
self._cache = disk_cache

key-value/key-value-aio/src/key_value/aio/stores/duckdb/store.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,11 @@ async def _close(self) -> None:
372372
def __del__(self) -> None:
373373
"""Clean up the DuckDB connection on deletion."""
374374
try:
375-
if not self._is_closed and not getattr(self, "_client_provided_by_user", False) and hasattr(self, "_connection"):
375+
if (
376+
not getattr(self, "_is_closed", False)
377+
and not getattr(self, "_client_provided_by_user", False)
378+
and hasattr(self, "_connection")
379+
):
376380
self._connection.close()
377381
self._is_closed = True
378382
except Exception: # noqa: S110

key-value/key-value-aio/src/key_value/aio/stores/dynamodb/store.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,12 @@ async def __aenter__(self) -> Self:
135135
async def __aexit__(
136136
self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
137137
) -> None:
138-
await super().__aexit__(exc_type, exc_value, traceback)
139-
# Only exit the client's context manager if the store created it
138+
# Exit the client's context manager before calling super().__aexit__() to avoid double-cleanup
140139
if not self._client_provided_by_user and self._client:
141-
await self._client.__aexit__(exc_type, exc_value, traceback)
140+
client = self._client
141+
self._client = None
142+
await client.__aexit__(exc_type, exc_value, traceback)
143+
await super().__aexit__(exc_type, exc_value, traceback)
142144

143145
@property
144146
def _connected_client(self) -> DynamoDBClient:
@@ -266,5 +268,4 @@ async def _delete_managed_entry(self, *, key: str, collection: str) -> bool:
266268
@override
267269
async def _close(self) -> None:
268270
"""Close the DynamoDB client."""
269-
if self._client:
270-
await self._client.__aexit__(None, None, None) # pyright: ignore[reportUnknownMemberType]
271+
# Client cleanup is handled in __aexit__

key-value/key-value-aio/src/key_value/aio/stores/memcached/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def __init__(
7070
"""
7171
client_provided = client is not None
7272

73-
if client:
73+
if client is not None:
7474
self._client = client
7575
else:
7676
self._client = Client(host=host, port=port)

key-value/key-value-aio/src/key_value/aio/stores/mongodb/store.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,6 @@ async def __aenter__(self) -> Self:
203203
@override
204204
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: # pyright: ignore[reportAny]
205205
await super().__aexit__(exc_type, exc_val, exc_tb)
206-
# Only exit the client's context manager if the store created it
207-
if not self._client_provided_by_user:
208-
await self._client.__aexit__(exc_type, exc_val, exc_tb)
209206

210207
@override
211208
async def _setup_collection(self, *, collection: str) -> None:
@@ -345,4 +342,4 @@ async def _delete_collection(self, *, collection: str) -> bool:
345342

346343
@override
347344
async def _close(self) -> None:
348-
await self._client.close()
345+
await self._client.__aexit__(None, None, None)

key-value/key-value-aio/src/key_value/aio/stores/rocksdb/store.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,7 @@ async def _delete_managed_entries(self, *, keys: Sequence[str], collection: str)
194194

195195
def __del__(self) -> None:
196196
if not getattr(self, "_client_provided_by_user", False):
197-
self._close_and_flush()
197+
try: # noqa: SIM105
198+
self._close_and_flush()
199+
except (AttributeError, Exception): # noqa: S110
200+
pass # Best-effort cleanup during finalization

key-value/key-value-aio/src/key_value/aio/stores/valkey/store.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,5 @@ async def _delete_managed_entries(self, *, keys: Sequence[str], collection: str)
168168
@override
169169
async def _close(self) -> None:
170170
if self._connected_client is not None:
171-
await self._client.close()
171+
await self._connected_client.close()
172+
self._connected_client = None

key-value/key-value-sync/src/key_value/sync/code_gen/stores/disk/store.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def __init__(
6969
raise ValueError(msg)
7070

7171
client_provided = disk_cache is not None
72+
self._client_provided_by_user = client_provided
7273

7374
if disk_cache:
7475
self._cache = disk_cache

key-value/key-value-sync/src/key_value/sync/code_gen/stores/duckdb/store.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,11 @@ def _close(self) -> None:
317317
def __del__(self) -> None:
318318
"""Clean up the DuckDB connection on deletion."""
319319
try:
320-
if not self._is_closed and (not getattr(self, "_client_provided_by_user", False)) and hasattr(self, "_connection"):
320+
if (
321+
not getattr(self, "_is_closed", False)
322+
and (not getattr(self, "_client_provided_by_user", False))
323+
and hasattr(self, "_connection")
324+
):
321325
self._connection.close()
322326
self._is_closed = True
323327
except Exception: # noqa: S110

key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,6 @@ def __enter__(self) -> Self:
201201
@override
202202
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: # pyright: ignore[reportAny]
203203
super().__exit__(exc_type, exc_val, exc_tb)
204-
# Only exit the client's context manager if the store created it
205-
if not self._client_provided_by_user:
206-
self._client.__exit__(exc_type, exc_val, exc_tb)
207204

208205
@override
209206
def _setup_collection(self, *, collection: str) -> None:
@@ -320,4 +317,4 @@ def _delete_collection(self, *, collection: str) -> bool:
320317

321318
@override
322319
def _close(self) -> None:
323-
self._client.close()
320+
self._client.__exit__(None, None, None)

0 commit comments

Comments
 (0)