-
Notifications
You must be signed in to change notification settings - Fork 261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clarification on transaction isolation and management #424
Comments
I don't have that much knowledge of connection internals, but as I understand the two calls of To get different connections maybe we can do this instead of passing the connection? async with db.transaction():
... Now each transaction will have different connection. |
I'm very much interested in the same question. While digging I found the 2 possible solutions to the problem in the script provided by @cochiseruhulessin use a new connection with: script
use the connection on transaction with: script
Both work, but neither is ideal (accessing protected members). Personally I think the second approach (use the connection on the transaction) feels much more natural and clear (similar to how pogi does it). And it would only require to expose the already existing separate connection on the transaction (core.py#L207). Side note: I need to make queries in fastAPI route handlers. The code in the documentation suggests to use one connection for all queries. How to start and use a transaction on this connection without impacting concurrent requests also accessing the DB? Or is it better to acquire a separate connection on each request as this example is suggesting? |
I agree that this isn't clear and the accessing transaction connection can be improved. PRs are welcome. As for your last comment, the reason why the docs say you should use a single connection is that the underlying connection is actually a connection pool. asyncpg Transaction docs here. |
Update: We already have support for specifying isolation-level here. So this should work for the isolation levels and needs to be documented: db = Database("postgresql://...")
await db.connect()
async with db.transaction(isolation="serialisable"):
db.execute("SELECT 1") But using the isolation level won't solve the issue in the example alone. |
Added isolation docs in #434 . |
I see this as another fallout from the current (databases <= 0.7.0) handling of ContextVar that I am fixing in #546. await asyncio.gather(
tx1(db.connection()),
tx2(db.connection())
)
As part of #546 I converted your example into a test, and it looked roughly like this: @pytest.mark.parametrize("database_url", DATABASE_URLS)
async def test_parallel_transaction_isolation(database_url):
metadata = sqlalchemy.MetaData()
notes = sqlalchemy.Table(
"notes",
metadata,
sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
sqlalchemy.Column("text", sqlalchemy.String(length=100)),
sqlalchemy.Column("completed", sqlalchemy.Boolean),
)
engine = sqlalchemy.create_engine(database_url)
metadata.create_all(engine)
setup = asyncio.Event()
done = asyncio.Event()
async def tx1(connection):
async with connection.transaction():
await db.execute(
notes.insert(), values={"id": 1, "text": "tx1", "completed": False}
)
setup.set()
await done.wait()
async def tx2(connection):
async with connection.transaction():
await setup.wait()
result = await db.fetch_all(notes.select())
assert result == [], result
done.set()
async with Database(database_url) as db:
async with db.connection() as conn:
await asyncio.gather(tx1(conn), tx2(conn)) And I have a few thoughts for you @cochiseruhulessin to ponder, minor things first:
Now the main point here:
|
Consider the following simulation of concurrent access:
This code should exit succesfully, but either fails with
cannot perform operation: another operation is in progress
(which is also weird because a new connection is requested) or at theassert
statement. Please provide some clarification regarding the expected transactional behavior and isolation of this module.The text was updated successfully, but these errors were encountered: