Damian Wysocki
https://docs.sqlalchemy.org/en/20/orm/contextual.html#using-thread-local-scope-with-web-applications
TO BE REMOVED
implicit transaction example
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
app = FastAPI()
async def get_db():
async with async_session() as session:
yield session
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
implicit transaction example
code
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
implicit transaction example
code
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
.
implicit transaction example
code
logs
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
DEBUG Connection checked out from pool
INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine SELECT (...)
implicit transaction example
code
logs
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
# exit function <-
DEBUG Connection checked out from pool
INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine SELECT (...)
INFO: 127.0.0.1:63630 - "GET /note?note_id=1 HTTP/1.1" 200 OK
INFO sqlalchemy.engine.Engine ROLLBACK
DEBUG Connection being returned to pool
DEBUG Connection rollback-on-return
implicit transaction example
code
database logs
Request validation
session is created
request
middleware
dependency injection
request execution
response generation
response
middleware
response validation
exception
handling
return response
async
exit
stack
simplified request lifecycle
implicit transaction example
implicit transaction summary
create session with dependency injection
connection is acquired on demand
transaction is created implicitly
connection is released after returning the response
rollback on return
possible solution
.
explicit transaction example
old code
database logs
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
async with database.begin():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
.
explicit transaction example
new code
database logs
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
async with database.begin():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
.
possible solution
code
database logs
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
async with database.begin():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
DEBUG Connection checked out from pool
INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine SELECT (...)
possible solution
code
database logs
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
async with database.begin():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
# exit context manager <-
DEBUG Connection checked out from pool
INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine SELECT (...)
INFO sqlalchemy.engine.Engine COMMIT
DEBUG Connection being returned to pool
DEBUG Connection rollback-on-return
explicit transaction example
code
database logs
@app.get("/notes/{note_id}")
async def get_note(database=Depends(get_db)):
async with database.begin():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
# exit context manager <-
# exit function <-
DEBUG Connection checked out from pool
INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine SELECT (...)
INFO sqlalchemy.engine.Engine COMMIT
DEBUG Connection being returned to pool
DEBUG Connection rollback-on-return
INFO: 127.0.0.1:63630 - "GET /note?note_id=1 HTTP/1.1" 200 OK
explicit transaction example
code
database logs
explicit transaction summary
create session with dependency injection
connection is acquired on demand
transaction is created implicitly
connection is released before returning the response
rollback on return
compare logs
compare logs
DEBUG Connection checked out from pool
INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine SELECT (...)
INFO sqlalchemy.engine.Engine COMMIT
DEBUG Connection being returned to pool
DEBUG Connection rollback-on-return
INFO: 127.0.0.1:63630 - "GET /note?note_id=1 HTTP/1.1" 200 OK
improved solution's logs
DEBUG Connection checked out from pool
INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine SELECT (...)
INFO: 127.0.0.1:63630 - "GET /note?note_id=1 HTTP/1.1" 200 OK
INFO sqlalchemy.engine.Engine ROLLBACK
DEBUG Connection being returned to pool
DEBUG Connection rollback-on-return
initial solution's logs
compare request lifecycle
Request validation
session is created
middleware
dependency injection
request execution
response geneation
middleware
response validation
exception
handling
return response
async
exit
stack
change me
Request validation
session is created
dependency injection
request execution
response geneation
middleware
response validation
exception
handling
return response
async
exit
stack
middleware
explicit transaction example
summary
more real life example
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
more real life example
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
Normal async function, right?
let's test it!
benchmarks
benchmarks
5 concurrent requests
pool size is equal to 5
1
2
3
4
5
6
time in seconds
benchmarks
10 concurrent requests
pool size is equal to 5
1
2
3
4
5
6
7
8
9
10
time in seconds
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(note_id: int, database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
solution
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
implicit transactions
solution
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
explicit transactions
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
requests
connections pool
time
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
old benchmarks
10 concurrent requests
1
2
3
4
5
6
7
8
9
10
time in seconds
benchmarks - for solution
10 concurrent requests
1
2
3
4
5
6
7
8
9
10
time in seconds
benchmarks - for solution
10 concurrent requests
1
2
3
4
5
6
7
8
9
10
time in seconds
What do programmers love the most?
diagrams!
add qrcode to diagrams
bonus - encode/databases
sqlalchemy
engine = create_async_engine(DATABASE_URL, pool_size=5, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("notes/{note_id}/synchronize")
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
encode/databases
database = databases.Database(DATABASE_URL)
@app.post("notes/{note_id}/synchronize")
async def synchronize_note():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
encode/databases
database = databases.Database(DATABASE_URL)
@app.on_event("startup")
async def startup():
await database.connect()
@app.on_event("shutdown")
async def shutdown():
await database.disconnect()
@app.post("notes/{note_id}/synchronize")
async def synchronize_note():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
benchmarks
1000 concurrent users
10 available connections
1 worker
benchmarks
change me
engine = create_async_engine(DATABASE_URL, pool_size=10, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
return result.scalar_one()
20
40
60
80
100
120
140
RPS (requests per second)
160
180
200
benchmarks
explicit transactions - sqlalchemy
engine = create_async_engine(DATABASE_URL, pool_size=10, max_overflow=0)
async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
@app.post("/notes/{note_id}/synchronize", response_model=Note)
async def synchronize_note(database=Depends(get_db)):
validate_note_id(note_id)
async with database.begin():
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
async with database.begin():
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
20
40
60
80
100
120
140
RPS (requests per second)
160
180
200
benchmarks
database = databases.Database(DATABASE_URL)
@app.post("notes/{note_id}/synchronize")
async def synchronize_note():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
encode/databases
20
40
60
80
100
120
140
RPS (requests per second)
160
180
200
benchmarks
database = databases.Database(DATABASE_URL)
@app.post("notes/{note_id}/synchronize")
async def synchronize_note():
validate_note_id(note_id)
query = sqlalchemy.select(notes).where(notes.c.id == note_id)
result = await database.execute(query)
updated_note_data = await external_api.sync_note(note_id) # takes around 5 seconds
query = sqlalchemy.update(notes).values(data=updated_note_data).where(notes.c.id == note_id)
result = await database.execute(query)
await database.commit()
return result.scalar_one()
encode/databases
20
40
60
80
100
120
140
RPS (requests per second)
160
180
200
summary
thanks you
let's connect