Repository navigation
Disconnect Hangs After Using Transactions #262
Description
This program hangs for me on the disconnect call (until I hit CTRL+C; tracebacks are attached later).
import asyncio
import databases
async def main():
db = Database(url)
await db.connect()
transaction = await db.transaction()
await transaction.start()
print(await db.execute('select 1'))
await transaction.commit() # or rollback, doesn't matter
await db.disconnect()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())The same program works if I remove the explicit transaction:
import asyncio
import databases
async def main():
db = Database(url)
await db.connect()
print(await db.execute('select 1'))
await db.disconnect()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())And it also works if I use a context manager:
import asyncio
import databases
async def main():
db = Database(url)
await db.connect()
async with db.transaction():
print(await db.execute('select 1'))
await db.disconnect()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())I'm using Python 3.8.1 on Mac OS X 10.14.6, and for the URL I used sqlite:////tmp/db.sqlite3 and postgrseql://user:1234@localhost:5432/test (on a standard postgres Docker container). These were the tracebacks after I hit CTRL+C on the hanging program:
# for sqlite3
Exception ignored in: <module 'threading' from '/Users/dan/.pyenv/versions/3.8.1/lib/python3.8/threading.py'>
Traceback (most recent call last):
File "/Users/dan/.pyenv/versions/3.8.1/lib/python3.8/threading.py", line 1388, in _shutdown
lock.acquire()
KeyboardInterrupt:
# for postgresql
Traceback (most recent call last):
File "a.py", line 19, in <module>
loop.run_until_complete(main())
File "/Users/dan/.pyenv/versions/3.8.1/lib/python3.8/asyncio/base_events.py", line 599, in run_until_complete
self.run_forever()
File "/Users/dan/.pyenv/versions/3.8.1/lib/python3.8/asyncio/base_events.py", line 567, in run_forever
self._run_once()
File "/Users/dan/.pyenv/versions/3.8.1/lib/python3.8/asyncio/base_events.py", line 1819, in _run_once
event_list = self._selector.select(timeout)
File "/Users/dan/.pyenv/versions/3.8.1/lib/python3.8/selectors.py", line 558, in select
kev_list = self._selector.control(None, max_ev, timeout)
KeyboardInterrupt
After fiddling a bit with the source code (specifically, I was trying to figure out why the context manager works, but the methods themselves don't), it seems like the problem might be with the documentation — here it says to await the db.transaction() as well as its start(), but that seems to cause the problem. If I drop the first await, this works:
import asyncio
import databases
async def main():
db = Database(url)
await db.connect()
transaction = db.transaction()
await transaction.start()
print(await db.execute('select 1'))
await transaction.commit() # or rollback, doesn't matter...
await db.disconnect()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())So maybe everything's working as intended, but then this tiny typo in the documentation should probably be fixed.
Beautiful library, by the way, thanks for making it happen :)
Activity
connebs commentedon Dec 27, 2020
I have noticed the same thing, though I actually was still not having success even with the adjustment used by @dan-gittik, as I was trying to use the low-level generator pattern.
I thought I was having success with a pattern like this:
But in retrospect it wasn't actually creating a transaction (rollbacks were not happening on exception).
Maybe I'm missing something, but I don't really understand how this code in databases makes sense:
The
start()method returns aTransaction, and__await__just callsstart().__await__()-> is this not entirely circular logic? Maybe I'm just not well versed enough in co-routines.arauter commentedon Jan 27, 2021
@dan-gittik your recommendation to use:
was a life safer 👍
goteguru commentedon Apr 27, 2021
In the source we can see Database.transaction() is not a coroutine, awaiting for it is unreasonable. It's just a thin wrapper returning the Transaction object for the current connection which is also an async context manager.
or
or
are equally correct.
await db.transaction()is not.(Should be fixed in the docs)
aminalaee commentedon Sep 13, 2021
Fixed by #390