Understanding context separation for multiple transactions #1167
-
I wanted to understand that how does piccolo understand the context of a transaction? The following example has been taken from the documentation (taken from https://piccolo-orm.readthedocs.io/en/latest/piccolo/tutorials/fastapi.html#transactions ): async def transaction():
async with DB.transaction() as transaction:
yield transaction
app = FastAPI()
@app.get("/bands/", dependencies=[Depends(transaction)])
async def get_bands():
return await Band.select() How does Piccolo handle the context of a transaction without the transaction object being passed around? Lets say we get 2 parallel calls for the same API then how are the queries for the transactions mapped separately? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
It uses something called Imagine it like a tree - if you create a transaction then any branches lower down (i.e. function calls) have access to it. Here's an example where we run 3 async functions, and each has its own transaction (the transactions don't interfere with each other): import asyncio
import random
from piccolo.engine.postgres import PostgresEngine
DB = PostgresEngine({"database": "piccolo"})
async def which_transaction(name: str):
# Sleep to simulate doing stuff:
await asyncio.sleep(random.random())
print(f"{name}=", id(DB.current_transaction.get()))
async def do_stuff(name: str):
await which_transaction(name=name)
async def simple_endpoint(name: str):
async with DB.transaction():
# Sleep to simulate doing stuff:
await asyncio.sleep(random.random())
await which_transaction(name=name)
# This automatically has access to the same transaction:
await do_stuff(name=name)
async def main():
await asyncio.gather(
simple_endpoint('a'),
simple_endpoint('b'),
simple_endpoint('c')
)
if __name__ == "__main__":
asyncio.run(main())
>>> python main.py
a= 4369774048
b= 4369775008
a= 4369774048
b= 4369775008
c= 4369775104
c= 4369775104 In your FastAPI example, the dependency should get triggered when the endpoint is called, which means all code within that endpoint is wrapped in the transaction. If another endpoint is called, it's wrapped in its own transaction. You don't need to worry about there being two concurrent endpoint calls - they are different async tasks (one isn't the parent of another), so they both have separate transactions. This might help too: https://piccolo-orm.com/blog/python-contextvars/ |
Beta Was this translation helpful? Give feedback.
It uses something called
contextvars
under the hood.Imagine it like a tree - if you create a transaction then any branches lower down (i.e. function calls) have access to it.
Here's an example where we run 3 async functions, and each has its own transaction (the transactions don't interfere with each other):