Stage 8 · L33
Choose sync or async by the actual library boundary
Core · original Session 8
Use the starter for this stage's focused examples. The cumulative transfer and solution belong at the stage-end capstone. Baseline checks pass; transfer checks initially fail. Downloads contain the matching native starter and solution for this stage.
async def is useful when a handler awaits compatible nonblocking I/O. It is not a general speed switch. A synchronous SQLAlchemy call still blocks its executing thread; putting it directly in an async handler can block the event loop. This core stage follows the original Session08 pattern: synchronous owned preparation, then async outbound HTTP.
prepare_lookup is a synchronous dependency and FastAPI runs it in the worker-thread facility. It resolves the allowed stored rental and returns a frozen Reference. The async route then awaits Gateway.lookup. The database session is already closed; no SQLAlchemy row or relationship crosses that await boundary.
The application lifespan owns one AsyncClient so connections can be reused and closed intentionally. Creating a client for every attempt discards pooling and makes ownership harder to review. A per-request session is not an appropriate place to keep an application HTTP client. The fixture verifies client.is_closed after lifespan exit.
Admission uses an asyncio.Semaphore to bound concurrent lookups in this one application event loop. It is not a global limit across multiple Uvicorn workers or hosts. The total lookup timeout includes time waiting for this local slot, HTTP attempts and retry sleeps. A local pool/admission shortage is classified separately from a provider outage.
An optional later extension demonstrates real async database sessions using psycopg's async dialect. That is additional implementation, not a claim that the original provider source requires async PostgreSQL. Choose a coherent resource model and test it; do not mix blocking calls into an event loop because a diagram contains await.
Follow the running code
Focused lesson example; see the end-of-stage capstone for the cumulative app · stage 08
import asyncio
async def wait(label):
await asyncio.sleep(0.02)
return label
async def main():
assert await asyncio.gather(wait('A'), wait('B')) == ['A', 'B']
asyncio.run(main())Predict and observe this focused example using the concepts explained above. Its boundary is stated in the focused answer.
Guided lab
- Read the explanation and predict the focused example’s outcome.
- Compare two sequential waits with a pair of overlapping waits. Explain why changing a blocking SQLAlchemy def helper to async def is insufficient.
- Compare the observed outcome with the focused answer and state its boundary.
Expected: await asyncio.sleep yields the event loop; a blocking call still blocks its executing thread. gather returns results in input order and can overlap independent waits, but keep concurrency bounded and preserve dependencies. The core keeps synchronous SQL preparation; this exercise contains no HTTP retry, payload policy or circuit breaker.
- An ordinary blocking library call blocks the event-loop thread even inside async def; await needs a compatible awaitable.
Focused exercise and answer
Complete this focused exercise before reading its answer. The full native transfer is introduced only at the end of the stage.
Your transfer task: Compare two sequential waits with a pair of overlapping waits. Explain why changing a blocking SQLAlchemy def helper to async def is insufficient.
- Compare two sequential waits with a pair of overlapping waits. Explain why changing a blocking SQLAlchemy def helper to async def is insufficient.
Inspect the matching answer
This answer addresses the focused exercise above; the cumulative implementation is shown only after the stage prerequisites.
await asyncio.sleep yields the event loop; a blocking call still blocks its executing thread. gather returns results in input order and can overlap independent waits, but keep concurrency bounded and preserve dependencies. The core keeps synchronous SQL preparation; this exercise contains no HTTP retry, payload policy or circuit breaker.Check your reasoning
Does changing def to async def make a synchronous driver nonblocking?
Show the explanation
No. The actual library call and execution boundary decide whether work blocks. A cooperative await lets other coroutines progress while it waits.
Reading progress
54 lessons remain open to guests. Marking a lesson read records reading only; it does not award assessment credit or a certificate.
Device reading marks require browser storage. Reading is always available.
Sign in or create an account to save separate account progress. Your current page is kept.
Your earlier place on this device suggests these lessons. No new lesson is marked read.