In race condition, two concurrent invocation of async with ConnectionWrapper(...) might fail, for example the process of create_connection() in SqliteClient is as follows:
async def create_connection(self, with_db: bool) -> None:
if not self._connection: # pragma: no branch
self._connection = aiosqlite.connect(self.filename, isolation_level=None)
self._connection.start()
await self._connection._connect()
self._connection._conn.row_factory = sqlite3.Row
for pragma, val in self.pragmas.items():
cursor = await self._connection.execute(f"PRAGMA {pragma}={val}")
await cursor.close()
self.log.debug(
"Created connection %s with params: filename=%s %s",
self._connection,
self.filename,
" ".join(f"{k}={v}" for k, v in self.pragmas.items()),
)
You can see that self._connection = aiosqlite.connect(self.filename, isolation_level=None) is given a value first, then it yields control when trying to connect to the database await self._connection._connect(), in the meantime, if a second task trying to acquire connection, it finds that self._connection already has a value (but it is not connected yet!), the second task passes self.ensure_connection() and acquire the lock! However, self.connection is not connected yet, so I got the following error reports:
File "/home/ubuntu/miniconda3/envs/crptrader-dev/lib/python3.11/site-packages/tortoise/models.py", line 957, in save
await executor.execute_update(self, update_fields)
File "/home/ubuntu/miniconda3/envs/crptrader-dev/lib/python3.11/site-packages/tortoise/backends/base/executor.py", line 317, in execute_update
await self.db.execute_query(
File "/home/ubuntu/miniconda3/envs/crptrader-dev/lib/python3.11/site-packages/tortoise/backends/sqlite/client.py", line 34, in translate_exceptions_
return await func(self, query, *args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/miniconda3/envs/crptrader-dev/lib/python3.11/site-packages/tortoise/backends/sqlite/client.py", line 142, in execute_query
start = connection.total_changes
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/miniconda3/envs/crptrader-dev/lib/python3.11/site-packages/aiosqlite/core.py", line 286, in total_changes
return self._conn.total_changes
^^^^^^^^^^
File "/home/ubuntu/miniconda3/envs/crptrader-dev/lib/python3.11/site-packages/aiosqlite/core.py", line 67, in _conn
raise ValueError("no active connection")
ValueError: no active connection
There is a race condition bug in ConnectionWrapper implementation:
We should acquire the lock before ensure_connection:
In race condition, two concurrent invocation of
async with ConnectionWrapper(...)
might fail, for example the process ofcreate_connection()
inSqliteClient
is as follows:You can see that
self._connection = aiosqlite.connect(self.filename, isolation_level=None)
is given a value first, then it yields control when trying to connect to the databaseawait self._connection._connect()
, in the meantime, if a second task trying to acquire connection, it finds thatself._connection
already has a value (but it is not connected yet!), the second task passesself.ensure_connection()
and acquire the lock! However,self.connection
is not connected yet, so I got the following error reports: