Fixed ModuleStorage close race by acquiring storage lock.
CI / Formatting (push) Successful in 5s
CI / Linting (push) Successful in 6s
CI / Tests (Python 3.12) (push) Successful in 2m49s
CI / Tests (Python 3.13) (push) Successful in 2m49s
CI / Tests (Python 3.14) (push) Successful in 2m49s
CI / Type Checking (push) Successful in 9s
CI / Spelling (push) Successful in 5s
CD / Publish Package (push) Successful in 9s
CD / Publish Image (push) Successful in 1m29s

This commit is contained in:
2026-04-24 20:05:33 -04:00
parent f20faa317e
commit d998429a76
2 changed files with 35 additions and 4 deletions
+30
View File
@@ -338,6 +338,36 @@ class TestConcurrency:
rows = await storage_with_table.fetch_all("SELECT id FROM items ORDER BY id")
assert [r["id"] for r in rows] == [1, 2]
async def test_close_waits_for_in_flight_connection_setup(
self, storage: ModuleStorage, monkeypatch: pytest.MonkeyPatch
) -> None:
"""_close() serializes against in-flight connection setup.
Without the lock, _close() can run between _ensure_connection's _closed
check and the _conn assignment, leaving _closed=True with a live
connection still bound to _conn that no later call ever closes.
"""
import aiosqlite # noqa: PLC0415 # imported here to monkeypatch its connect
release = asyncio.Event()
real_connect = aiosqlite.connect
async def gated_connect(*args: Any, **kwargs: Any) -> aiosqlite.Connection:
await release.wait()
return await real_connect(*args, **kwargs)
monkeypatch.setattr(aiosqlite, "connect", gated_connect)
op_task = asyncio.create_task(storage.execute("SELECT 1"))
await asyncio.sleep(0)
close_task = asyncio.create_task(storage._close())
await asyncio.sleep(0)
release.set()
await asyncio.gather(op_task, close_task)
assert storage._closed is True
assert storage._conn is None
class TestStorageError:
"""Invalid SQL raises StorageError wrapping the underlying error."""