peewee-async
peewee-async copied to clipboard
'NoneType' object has no attribute 'cursor'
Looks like there is a race condition in the snippet:
# This is a part of code from AsyncDatabase.cursor_async
try:
# this line throws the error
return (await self._async_conn.cursor(conn=conn))
except:
await self.close_async()
raise
_async_conn can't be None here by design because it has been checked in connect_async method. I can guess how it happens:
- A and B connects are awaited in the line
await self.close_async() - A run the coroutine, closes the connection, does
_async_conn = None, B is still waiting on the same line - Connect C goes to open a connect, sets a future and waits in the
await conn.connect()line - Connect B goes in
close_asyncand waits for the future to complete. Connect D goes to open a connect insideconnect_asyncand waits for the future to complete. - Connect C completes opening, sets _async_conn not None and releases the future.
- Connect B receives control, sets _async_conn to None and closes the connection.
- Connect D receives control, exits connect_async and receives an error, because _async_conn is None
related to #114
Found a way to reproduce the bug. Inside AsyncPostgresqlConnection.cursor function add the lines:
async def cursor(self, conn=None, *args, **kwargs):
"""Get cursor for connection from pool.
"""
# lines we have added
import random
choice = random.randint(1, 5)
if choice == 5:
raise Exception("some network error") # network error imitation
in_transaction = conn is not None
if not conn:
conn = await self.acquire()
cursor = await conn.cursor(*args, **kwargs)
cursor.release = functools.partial(
self.release_cursor, cursor,
in_transaction=in_transaction)
return cursor
Create a simple http application with select query on some endpoint. Use yandex-tank to create rps load on the application. We have NoneType' object has no attribute 'cursor soon. if write python with manager.atomic() inside the endpoint code we have "cursor already closed error