Skip to content

Commit 3cad8b5

Browse files
Add timeout to _run_sync to prevent indefinite blocking
Use the connection timeout for future.result() so the calling thread doesn't block forever if the server hangs or the event loop dies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a2ec6e2 commit 3cad8b5

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

src/dqlitedbapi/connection.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ def _run_sync(self, coro: Any) -> Any:
5858
"""
5959
loop = self._ensure_loop()
6060
future = asyncio.run_coroutine_threadsafe(coro, loop)
61-
return future.result()
61+
try:
62+
return future.result(timeout=self._timeout)
63+
except TimeoutError as e:
64+
future.cancel()
65+
raise OperationalError(f"Operation timed out after {self._timeout} seconds") from e
6266

6367
async def _get_async_connection(self) -> DqliteConnection:
6468
"""Get or create the underlying async connection."""

tests/test_run_sync_timeout.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Tests for _run_sync timeout behavior."""
2+
3+
import asyncio
4+
5+
import pytest
6+
7+
from dqlitedbapi.connection import Connection
8+
from dqlitedbapi.exceptions import OperationalError
9+
10+
11+
class TestRunSyncTimeout:
12+
def test_run_sync_times_out(self) -> None:
13+
"""_run_sync should raise OperationalError after timeout."""
14+
conn = Connection("localhost:9001", timeout=0.1)
15+
16+
async def hang_forever() -> None:
17+
await asyncio.sleep(999)
18+
19+
with pytest.raises(OperationalError, match="timed out"):
20+
conn._run_sync(hang_forever())

0 commit comments

Comments
 (0)