Asynchronous Python lets one process make progress on other work while a coroutine waits for network or disk I/O. It does not make CPU work faster, make blocking libraries non-blocking, or remove resource limits. Correct async systems bound concurrency, set timeouts, propagate cancellation, and keep ownership visible.
An async def call creates a coroutine. When the running coroutine reaches an await on incomplete I/O, it yields control to the event loop. The loop can resume another ready task. If you call blocking code inside the loop, every coroutine waits.
The semaphore protects the downstream provider and your own sockets. The timeout bounds the whole operation. In real code, choose limits from measured capacity and provider quotas.
Use synchronous code when the dependency is synchronous, traffic is modest, or simplicity improves correctness. FastAPI can run sync route functions in a thread pool. Do not label a function async while calling a blocking database or HTTP client inside it; use a compatible async client or isolate blocking work deliberately.
CPU-heavy document parsing or embedding preprocessing belongs in a worker process or specialized service, not on the event loop.
Model calls are high-latency I/O and may stream. Unbounded parallel calls can exhaust money, quotas, connections, and memory. Apply per-tenant quotas, global concurrency limits, timeouts, cancellation, and durable job state. Cancelling an HTTP response does not guarantee a provider stopped billing or a tool stopped executing.
Modify the example so one ticket raises an error and another exceeds the timeout. Record which tasks complete, fail, or cancel; add cleanup logging in finally.
Acceptance criteria: the program never waits forever, concurrency never exceeds three, and failure behavior is explained from observed output.