You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When using @transaction() as a decorator, all calls to the function use the same connection and therefor concurrent calls share the transaction scope, causing all kinds of race-condition mischief.
This is not an issue when using the transaction as a context manager as then the connection is allotted during function call rather than at import time.
Example, calling update_person multiple times concurrently here causes error:
asyncpg.exceptions._base.InterfaceError: cannot commit; the transaction is already committed
importasyncioimportcontextvarsfromdatabasesimportDatabasedb=Database("postgresql://dev:dev@localhost:6808/dev")
asyncdefinit():
create_table=""" CREATE TABLE person ( id INTEGER PRIMARY KEY, name VARCHAR(100) ); """awaitdb.connect()
awaitdb.execute(create_table)
awaitdb.execute("INSERT INTO person (id, name) VALUES (1, 'bob')")
@db.transaction()asyncdefupdate_person():
awaitdb.fetch_one("SELECT * FROM person WHERE id = 1")
awaitdb.execute("UPDATE person SET name='joe' WHERE id = 1")
asyncdefupdate_concurrently():
awaitinit()
""" Call `update_person` 6 times concurrently, simulating e.g. concurrent requests in a web app """tasks= []
foriinrange(6):
ctx=contextvars.Context()
tasks.append(ctx.run(asyncio.create_task, update_person()))
awaitasyncio.gather(*tasks)
loop=asyncio.get_event_loop()
loop.run_until_complete(update_concurrently())
The text was updated successfully, but these errors were encountered:
This bug with @db.transaction should be fixed in #546, and be safe for concurrent usage again. When that's released, please let me know if this still affects you.
I have tested your MVE on the branch and I'm not seeing that error anymore. Thank you for a functional and complete example! And props for noticing that is was due to contextvars! 👏
When using
@transaction()
as a decorator, all calls to the function use the same connection and therefor concurrent calls share the transaction scope, causing all kinds of race-condition mischief.This is not an issue when using the transaction as a context manager as then the connection is allotted during function call rather than at import time.
Example, calling
update_person
multiple times concurrently here causes error:The text was updated successfully, but these errors were encountered: