Member-only story
asyncpg vs SQLAlchemy ORM—The 9x Slowdown Nobody Warns You About
Async doesn’t mean fast. Your ORM might be quietly throwing away everything asyncpg gives you.
We migrated a FastAPI service from sync SQLAlchemy to async SQLAlchemy, expecting a performance win. Async is supposed to be faster, right? Non-blocking I/O, more concurrent requests per worker, better resource utilization.
Our load test showed the opposite. The async version was slower per-request than the sync version had been. Not by a little — a specific endpoint that fetched 500 rows and mapped them to ORM objects went from 340ms to 890ms.
I assumed we’d misconfigured something. Wrong connection pool size, maybe, or a missing index. I profiled the endpoint with cProfile and found the answer: the async ORM layer itself was the bottleneck. Object instantiation, identity map tracking, and attribute loading — the same overhead that exists in sync SQLAlchemy — was still there in async SQLAlchemy. We’d added the complexity of async without eliminating the cost of the ORM.
This isn’t a reason to avoid async or avoid SQLAlchemy. It’s a reason to understand exactly what you’re paying for at each layer, so you can make an informed choice instead of assuming “async” automatically means “fast.”









