Skip to content

Commit 5e55312

Browse files
committed
fix(integrations): close AsyncDaytona client in DaytonaEnvironment.close()
DaytonaEnvironment.close() deleted the sandbox and then dropped the AsyncDaytona client by setting self._client = None, without awaiting the client's close(). That client owns the underlying aiohttp session plus the REST and toolbox API clients, so discarding the reference leaked sockets on every create/close cycle. The Daytona SDK documents that AsyncDaytona.close() should be called to release those HTTP sessions and avoid resource leaks (or the client used as an async context manager). Await self._client.close() before clearing the reference. close() is idempotent, so the existing double-close no-op behavior is preserved. The unit test now gives the mock client an AsyncMock close() and asserts DaytonaEnvironment.close() awaits it exactly once.
1 parent e6df097 commit 5e55312

2 files changed

Lines changed: 9 additions & 0 deletions

File tree

src/google/adk/integrations/daytona/_daytona_environment.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ async def close(self) -> None:
9696
if self._sandbox is not None:
9797
await self._sandbox.delete()
9898
self._sandbox = None
99+
if self._client is not None:
100+
# Close the AsyncDaytona client to release its underlying HTTP
101+
# sessions and avoid leaking sockets across create/close cycles.
102+
await self._client.close()
99103
self._client = None
100104
self._is_initialized = False
101105

tests/unittests/integrations/daytona/test_daytona_environment.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def _daytona_patch(sandbox: mock.MagicMock):
4747
"""Patch AsyncDaytona to return a mock client."""
4848
mock_client = mock.MagicMock(name="AsyncDaytona")
4949
mock_client.create = mock.AsyncMock(return_value=sandbox)
50+
mock_client.close = mock.AsyncMock()
5051

5152
with mock.patch.object(daytona, "AsyncDaytona", autospec=True) as mock_class:
5253
mock_class.return_value = mock_client
@@ -103,14 +104,18 @@ async def test_close_deletes_sandbox_and_is_idempotent(daytona_patch, sandbox):
103104
env = DaytonaEnvironment()
104105
await env.initialize()
105106
assert env.is_initialized is True
107+
client = daytona_patch.return_value
106108
await env.close()
107109
sandbox.delete.assert_awaited_once()
110+
client.close.assert_awaited_once()
108111
assert env._sandbox is None
112+
assert env._client is None
109113
assert env.is_initialized is False
110114

111115
# Second close is a no-op.
112116
await env.close()
113117
sandbox.delete.assert_awaited_once()
118+
client.close.assert_awaited_once()
114119

115120

116121
async def test_working_dir_requires_initialize():

0 commit comments

Comments
 (0)