Механика. cancel() лишь помечает задачу; при следующем шаге в неё бросается CancelledError из того await, на котором она стоит. Корутина может выполнить очистку:
async def worker():
conn = await pool.acquire()
try:
await conn.execute(...)
finally:
await pool.release(conn) # выполнится и при отмене
Глотать нельзя. Если поймать CancelledError и не пробросить, вызывающий будет считать, что задача отменена, а она продолжит работу; TaskGroup и asyncio.timeout сломаются. Поймали для логирования — raise в конце.
Таймауты:
async with asyncio.timeout(2): # Python 3.11
await fetch()
# или
await asyncio.wait_for(fetch(), timeout=2)
При истечении внутренняя задача отменяется, наружу выходит TimeoutError.
shield. await asyncio.shield(save()) — внешняя отмена не прервёт сохранение, но вызывающий всё равно получит CancelledError.
Практика: таймаут на каждый внешний вызов; отмена распространяется сверху вниз — при отключении клиента фреймворк отменяет обработчик, и это должно корректно освобождать ресурсы.