Introducing Asychronous SQLAlchemy
Transcript of Introducing Asychronous SQLAlchemy
![Page 1: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/1.jpg)
Introducing AsychronousSQLAlchemy
Introducing Asynchronous SQLAlchemySebastiaan Zeeff — EuroPython 2021
![Page 2: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/2.jpg)
The ingredients of this talk
• Synchronous vs Asynchronous Input/Output
• A comparison between synchronous and asynchronous SQLAlchemy
• Managing implicit I/O: Eager vs Lazy loading
![Page 3: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/3.jpg)
Personal introduction
• Sebastiaan Zeeff (35), The Netherlands
• Codesmith and developer for Ordina Pythoneers
• Owner of Python Discord
![Page 4: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/4.jpg)
Synchronous vs Asynchronous Input/Output
![Page 5: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/5.jpg)
Synchronous vs Asynchronous Input/Output
![Page 6: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/6.jpg)
Synchronous vs Asynchronous Input/Output
![Page 7: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/7.jpg)
Synchronous vs Asynchronous Input/Output
![Page 8: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/8.jpg)
Synchronous vs Asynchronous Input/Output
![Page 9: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/9.jpg)
Synchronous vs Asynchronous Input/Output
![Page 10: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/10.jpg)
Synchronous vs Asynchronous Input/Output
SQL
![Page 11: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/11.jpg)
Synchronous vs Asynchronous Input/Output
![Page 12: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/12.jpg)
Synchronous vs Asynchronous Input/Output
![Page 13: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/13.jpg)
Synchronous vs Asynchronous Input/Output
Event loop
![Page 14: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/14.jpg)
Synchronous vs Asynchronous Input/Output
Event loop
Task 1
Current task
![Page 15: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/15.jpg)
Synchronous vs Asynchronous Input/Output
Event loop
Task 1
Task 2 Task 3 Task 4
Scheduled tasks
Current task
![Page 16: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/16.jpg)
Synchronous vs Asynchronous Input/Output
Task 1
Event loop
Scheduled tasks
Current task
Task 2 Task 3 Task 4
![Page 17: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/17.jpg)
Synchronous vs Asynchronous Input/Output
Task 1
Event loop
Scheduled tasks
Current task
Task 2 Task 3 Task 4
![Page 18: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/18.jpg)
Synchronous vs Asynchronous Input/Output
Task 1
Event loop
Scheduled tasks
Current task
Task 2 Task 3 Task 4
![Page 19: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/19.jpg)
Synchronous vs Asynchronous Input/Output
Task 1
Event loop
Scheduled tasks
Current task
Task 2 Task 3 Task 4
![Page 20: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/20.jpg)
Synchronous vs Asynchronous Input/Output
Task 1
Event loop
Scheduled tasks
Current task
Task 2 Task 3 Task 4
![Page 21: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/21.jpg)
• Asyncio is about how we schedule input/put asynchronously in our SQLAlchemy app
DatabaseSQLAlchemy App
![Page 22: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/22.jpg)
• Asyncio is about how we schedule input/put asynchronously in our SQLAlchemy app
• The database queries and responses you generate will mostly stay the same (although…).
DatabaseSQLAlchemy App
SQL Query
Database response
![Page 23: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/23.jpg)
• Asyncio is about how we schedule input/put asynchronously in our SQLAlchemy app
• The database queries and responses you generate will mostly stay the same (although…).
• This means that you can (mostly) build your queries in the way you're already used to!
DatabaseSQLAlchemy App
SQL Query
Database response
![Page 24: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/24.jpg)
Asynchronous SQLAlchemy in Action
• I'll compare the sync and async versions of the same action
• Versions used:
• Python 3.9.6
• SQLAlchemy 1.4.22• The asyncio extension is currently considered to be in a beta release
• asyncpg 0.23.0 (async adapter); Psycopg2 2.9.1 (sync adapter)
• PostgreSQL 13.3 in an alpine-based Docker container
![Page 25: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/25.jpg)
Setting up the engine
Python REPL 3.9.6>>> import sqlalchemy>>> engine = sqlalchemy.create_engine(... "postgresql+psycopg2://europython:[email protected]:9876/europython",... echo=True,... future=True,... )>>>
asyncio REPL 3.9.6>>> from sqlalchemy.ext import asyncio as asyncio_ext>>> engine = asyncio_ext.create_async_engine(... "postgresql+asyncpg://europython:[email protected]:9876/europython",... echo=True,... future=True,... )>>>
![Page 26: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/26.jpg)
Executing a simple SQL statement
Python REPL 3.9.6>>> statement = sqlalchemy.text("SELECT 'Hello, EuroPython 2021!'")>>> with engine.connect() as conn:... result = conn.execute(statement)... >>> print(result.scalar())Hello, EuroPython 2021!
asyncio REPL 3.9.6>>> statement = sqlalchemy.text("SELECT 'Hello, EuroPython 2021!'")>>> async with engine.connect() as conn:... result = await conn.execute(statement)... >>> print(result.scalar())Hello, EuroPython 2021!
![Page 27: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/27.jpg)
Eager vs Lazy loading
• That looks simple, what's the catch?
![Page 28: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/28.jpg)
Eager vs Lazy loading
• That looks simple, what's the catch?
• You can't just rely on implicit I/O!
![Page 29: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/29.jpg)
A simple ORM model
class Traveler(Base):"""A model representing a traveler."""
__tablename__ = "traveler"
id = Column(Integer, primary_key=True)created_at = Column(DateTime, server_default=func.now())name = Column(String(128))age = Column(Integer)
![Page 30: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/30.jpg)
Lazy loading vs Eager loading (server defaults)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35)>>> with orm.Session(engine) as session:... with session.begin():... session.add(sebastiaan)...... print("=" * 64)... print("Sebastiaan was created at:", sebastiaan.created_at)
![Page 31: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/31.jpg)
Lazy loading vs Eager loading (server defaults)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35)>>> with orm.Session(engine) as session:... with session.begin():... session.add(sebastiaan)...... print("=" * 64)... print("Sebastiaan was created at:", sebastiaan.created_at)BEGIN (implicit)INSERT INTO traveler (name, age) VALUES (%(name)s, %(age)s) RETURNING traveler.id[generated in 0.00026s] {'name': 'Sebastiaan', 'age': 35}COMMIT================================================================
![Page 32: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/32.jpg)
Lazy loading vs Eager loading (server defaults)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35)>>> with orm.Session(engine) as session:... with session.begin():... session.add(sebastiaan)...... print("=" * 64)... print("Sebastiaan was created at:", sebastiaan.created_at)BEGIN (implicit)INSERT INTO traveler (name, age) VALUES (%(name)s, %(age)s) RETURNING traveler.id[generated in 0.00026s] {'name': 'Sebastiaan', 'age': 35}COMMIT================================================================BEGIN (implicit)SELECT traveler.id AS traveler_id, traveler.created_at AS traveler_created_at, traveler.name #...WHERE traveler.id = %(pk_1)s[generated in 0.00018s] {'pk_1': 1}Sebastiaan was created at: 2021-07-28 09:42:06.723638ROLLBACK
![Page 33: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/33.jpg)
Lazy loading vs Eager loading (server defaults)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35)>>> async_session = orm.sessionmaker(... engine,... expire_on_commit=False,... class_=asyncio_ext.AsyncSession...)>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)... print("=" * 64)... print("Sebastiaan was created at:", sebastiaan.created_at)
![Page 34: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/34.jpg)
Lazy loading vs Eager loading (server defaults)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35)>>> async_session = orm.sessionmaker(... engine,... expire_on_commit=False,... class_=asyncio_ext.AsyncSession...)>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)... print("=" * 64)... print("Sebastiaan was created at:", sebastiaan.created_at)BEGIN (implicit)INSERT INTO traveler (name, age) VALUES (%s, %s) RETURNING traveler.id[generated in 0.00049s] ('Sebastiaan', 35)COMMIT================================================================
![Page 35: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/35.jpg)
Lazy loading vs Eager loading (server defaults)
>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)... print("=" * 64)... print("Sebastiaan was created at:", sebastiaan.created_at)...BEGIN (implicit)INSERT INTO traveler (name, age) VALUES (%s, %s) RETURNING traveler.id[generated in 0.00049s] ('Sebastiaan', 35)COMMIT================================================================
![Page 36: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/36.jpg)
Lazy loading vs Eager loading (server defaults)
>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)... print("=" * 64)... print("Sebastiaan was created at:", sebastiaan.created_at)BEGIN (implicit)INSERT INTO traveler (name, age) VALUES (%s, %s) RETURNING traveler.id[generated in 0.00049s] ('Sebastiaan', 35)COMMIT================================================================BEGIN (implicit)SELECT traveler.created_at AS traveler_created_atFROM travelerWHERE traveler.id = %s[generated in 0.00061s] (2,)ROLLBACKTraceback (most recent call last):
...sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_() here. Was IO attempted in an unexpected place? (Background on this error at: https://sqlalche.me/e/14/xd2s)
![Page 37: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/37.jpg)
Working with ORM models
class Traveler(Base):"""A model representing a traveler."""
__tablename__ = "traveler"
id = Column(Integer, primary_key=True)created_at = Column(DateTime, server_default=func.now())name = Column(String(128))age = Column(Integer)
![Page 38: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/38.jpg)
Working with ORM models
class Traveler(Base):"""A model representing a traveler."""
__tablename__ = "traveler"
id = Column(Integer, primary_key=True)created_at = Column(DateTime, server_default=func.now())name = Column(String(128))age = Column(Integer)
__mapper_args__ = {"eager_defaults": True}
![Page 39: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/39.jpg)
Lazy loading vs Eager loading (server defaults)
>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)... print("=" * 64)... print("Sebastiaan was created at:", sebastiaan.created_at)BEGIN (implicit)INSERT INTO traveler (name, age) VALUES (%s, %s) RETURNING traveler.id, traveler.created_at[generated in 0.00040s] ('Sebastiaan', 35)COMMIT================================================================Sebastiaan was created at: 2021-07-28 10:15:32.675294
![Page 40: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/40.jpg)
Models with a relationship
class Traveler(Base):"""A model representing a traveler."""
__tablename__ = "traveler"
id = Column(Integer, primary_key=True)name = Column(String(128))age = Column(Integer)destination_id = Column(Integer, ForeignKey("country.id"))destination = orm.relationship("Country")
class Country(Base):"""A model representing a country."""
__tablename__ = "country"
id = Column(Integer, primary_key=True)name = Column(String(128))
![Page 41: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/41.jpg)
Executing a simple SQL statement
>>> sebastiaan = Traveler(name="Sebastiaan", age=35, destination=Country(name="Norway"))>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)...
![Page 42: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/42.jpg)
Eager vs Lazy loading (relationships)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35, destination=Country(name="Norway"))>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)...>>> statement = sqlalchemy.select(Traveler).where(Traveler.name == "Sebastiaan")
![Page 43: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/43.jpg)
Eager vs Lazy loading (relationships)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35, destination=Country(name="Norway"))>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)...>>> statement = sqlalchemy.select(Traveler).where(Traveler.name == "Sebastiaan")>>> async with async_session() as session:... result = await session.execute(statement)... sebastiaan = result.scalar()...>>>
![Page 44: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/44.jpg)
Eager vs Lazy loading (relationships)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35, destination=Country(name="Norway"))>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)...>>> statement = sqlalchemy.select(Traveler).where(Traveler.name == "Sebastiaan")>>> async with async_session() as session:... result = await session.execute(statement)... sebastiaan = result.scalar()...>>> print(sebastiaan)Traveler(destination=NOT_LOADED, id=1, name='Sebastiaan', age=35, destination_id=1)
![Page 45: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/45.jpg)
Eager vs Lazy loading (relationships)
>>> sebastiaan = Traveler(name="Sebastiaan", age=35, destination=Country(name="Norway"))>>> async with async_session() as session:... async with session.begin():... session.add(sebastiaan)...>>> statement = sqlalchemy.select(Traveler).where(Traveler.name == "Sebastiaan")>>> async with async_session() as session:... result = await session.execute(statement)... sebastiaan = result.scalar()...>>> print(sebastiaan)Traveler(destination=NOT_LOADED, id=1, name='Sebastiaan', age=35, destination_id=1)>>> print(sebastiaan.destination) # ERROR! Requires I/O to lazily load destination!
![Page 46: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/46.jpg)
Eager vs Lazy loading (relationships)
>>> statement = (... sqlalchemy.select(Traveler)... .where(Traveler.name == "Sebastiaan")... .options(orm.joinedload(Traveler.destination))...)
![Page 47: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/47.jpg)
Eager vs Lazy loading (relationships)
>>> statement = (... sqlalchemy.select(Traveler)... .where(Traveler.name == "Sebastiaan")... .options(orm.joinedload(Traveler.destination))...)>>> async with async_session() as session:... result = await session.execute(statement)... sebastiaan = result.scalar()...
![Page 48: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/48.jpg)
Eager vs Lazy loading (relationships)
>>> statement = (... sqlalchemy.select(Traveler)... .where(Traveler.name == "Sebastiaan")... .options(orm.joinedload(Traveler.destination))...)>>> async with async_session() as session:... result = await session.execute(statement)... sebastiaan = result.scalar()...>>> print(sebastiaan)Traveler(
destination=Country(id=1, name='Norway'), id=1, name='Sebastiaan', age=35, destination_id=1)
![Page 49: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/49.jpg)
Eager vs Lazy loading (relationships)
>>> statement = (... sqlalchemy.select(Traveler)... .where(Traveler.name == "Sebastiaan")... .options(orm.joinedload(Traveler.destination))...)>>> async with async_session() as session:... result = await session.execute(statement)... sebastiaan = result.scalar()...>>> print(sebastiaan)Traveler(
destination=Country(id=1, name='Norway'), id=1, name='Sebastiaan', age=35, destination_id=1)>>> print("Sebastiaan is traveling to:", sebastiaan.destination.name)Sebastiaan is traveling to Norway
![Page 50: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/50.jpg)
Using `run_sync`
• Wat if I want to run something that uses synchronous I/O functions?
![Page 51: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/51.jpg)
Using `run_sync`
• Wat if I want to run something that uses synchronous I/O functions?
• You can use AsyncSession.run_sync!
>>> async with asyn_session() as session:... await session.run_sync(Base.metadata.create_all)
![Page 52: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/52.jpg)
Using `run_sync`
• Wat if I want to run something that uses synchronous I/O functions?
• You can use AsyncSession.run_sync!
• For example, you can use the `MetaData.create_all` function like this:
>>> async with async_session() as session:... await session.run_sync(Base.metadata.create_all)
![Page 53: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/53.jpg)
Using `run_sync`
• Wat if I want to run something that uses synchronous I/O functions?
• You can use AsyncSession.run_sync!
• For example, you can use the `MetaData.create_all` function like this:
• This only works for asynchronous adapter functions!
>>> async with async_session() as session:... await session.run_sync(Base.metadata.create_all)
![Page 54: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/54.jpg)
Summary
• Most of your knowledge of SQLAlchemy is directly transferable
• You need to think carefully about operations that perform I/O
• You can still run synchronous database I/O functions with run_sync
![Page 55: Introducing Asychronous SQLAlchemy](https://reader033.fdocuments.net/reader033/viewer/2022052301/6288ff80e55d8e051e130163/html5/thumbnails/55.jpg)
Introducing AsychronousSQLAlchemy
Questions?Sebastiaan Zeeff — EuroPython 2021