queryspy
N+1 and query-budget detection for SQLAlchemy 2.0 — sync and async.
N+1 detected: 11 queries for User.addresses (lazy load)
triggered from app/services/users.py:28 in list_users()
SELECT address.id AS address_id, address.email AS address_email, ...
fix: .options(selectinload(User.addresses))
Point it at the test suite you already have. pytest --queryspy-strict
fails any test that triggers an N+1 and names the line of your code responsible,
with a fix you can paste. Async included — attribution walks the greenlet chain
SQLAlchemy runs lazy loads inside.