Async SQLAlchemy pitfalls
Lazy loading fires SQL on attribute access โ that's the trap
Module 25's WalkerTable had no relationships. Add one โ WalkerTable.bookings pointing at a new BookingTable โ and SQLAlchemy's default behavior is lazy loading: walker.bookings looks like a plain attribute read, but the first time you touch it, SQLAlchemy quietly runs a new SQL query to fetch those rows, right there, wherever that attribute access happens to sit in your code.
With the synchronous engine from module 25, that's merely a hidden query โ sloppy (it can create the N+1 problem: one query per row instead of one query total), but it works. With the async engine, it doesn't work at all: firing a query requires an await, and walker.bookings is a plain attribute access with no await anywhere near it. SQLAlchemy can't insert one on your behalf. The result is sqlalchemy.exc.MissingGreenlet โ a greenlet_spawn has not been called error โ raised the moment you read walker.bookings, not when you built the original query.