Added async lock to database transactions to prevent concurrent transaction conflicts.
CI / Formatting (push) Successful in 5s
CI / Linting (push) Successful in 4s
CI / Tests (push) Successful in 22s
CI / Type Checking (push) Successful in 11s
CI / Spelling (push) Successful in 5s

This commit is contained in:
2026-03-23 09:49:44 -04:00
parent 53b8bd3fae
commit ca3011011a
+13 -9
View File
@@ -14,6 +14,7 @@
"""Async SQLite database access for Crabstero's persistent storage.""" """Async SQLite database access for Crabstero's persistent storage."""
import asyncio
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from typing import TYPE_CHECKING, NamedTuple, Self from typing import TYPE_CHECKING, NamedTuple, Self
@@ -109,21 +110,24 @@ class Database:
:param connection: An open aiosqlite connection. :param connection: An open aiosqlite connection.
""" """
self._connection = connection self._connection = connection
self._tx_lock = asyncio.Lock()
@asynccontextmanager @asynccontextmanager
async def _transaction(self) -> AsyncIterator[None]: async def _transaction(self) -> AsyncIterator[None]:
"""Begin an immediate write transaction. """Begin an immediate write transaction.
Commits on success, rolls back on error. Acquires an async lock so that only one transaction runs at a
time, then commits on success or rolls back on error.
""" """
await self._connection.execute("BEGIN IMMEDIATE") async with self._tx_lock:
try: await self._connection.execute("BEGIN IMMEDIATE")
yield try:
except BaseException: yield
await self._connection.rollback() except BaseException:
raise await self._connection.rollback()
else: raise
await self._connection.commit() else:
await self._connection.commit()
@classmethod @classmethod
async def connect(cls, path: str) -> Self: async def connect(cls, path: str) -> Self: