В веб-обработчике отменяется внешняя задача запроса, но начатую операцию нужно довести до конца. Что именно изменяет asyncio.shield в отношении отмены?
asyncio.shield не даёт отмене внешней задачи автоматически отменить защищаемую внутреннюю задачу. Внешняя корутина при этом всё равно получает CancelledError и может завершиться, пока внутренняя операция продолжает выполняться.
shield защищает только от отмены через конкретное внешнее ожидание. Он не делает операцию неотменяемой и не гарантирует её выполнение после закрытия event loop.
Асинхронные операции часто образуют цепочку ожиданий: обработчик ждёт внутреннюю задачу, а вызывающий код может отменить обработчик. Обычная передача отмены по этой цепочке удобна для освобождения ресурсов, но опасна для коротких критичных операций, например фиксации результата или записи аудита.
asyncio.shield существует как примитив для разделения жизненного цикла внешней задачи и конкретной внутренней операции. Он не отменяет кооперативную модель asyncio, а позволяет явно выбрать, какая часть работы должна пережить отмену ожидания.
Без защиты отмена обработчика обычно распространяется на задачу, которую он ожидает. Если операция уже отправила данные или изменила состояние, её прерывание может оставить систему в промежуточном состоянии.
Противоположная ошибка — считать shield гарантией завершения. Защищённая задача всё ещё может быть отменена напрямую, завершиться с исключением или быть прервана закрытием event loop. Кроме того, если внешняя корутина немедленно завершится после отмены, результат внутренней задачи может остаться необработанным.
При ожидании через asyncio.shield отменяется объект внешнего ожидания, но не сама внутренняя задача. Внешняя корутина получает CancelledError и должна решить, как поступить: сразу завершиться, дождаться внутренней операции перед повторным выбрасыванием исключения или передать работу отдельному надёжному механизму.
Здесь отмена handler не отменяет write_audit. Обработчик сначала дожидается завершения записи, затем повторно сообщает вызывающему коду об отмене. Для реального сервиса такое ожидание обычно ограничивают отдельным тайм-аутом.
Если вместо задачи передать в shield корутину, asyncio создаст для неё задачу. На практике лучше заранее сохранить объект задачи в сильной ссылке: event loop хранит задачи слабо, поэтому без другой ссылки фоновая задача может быть собрана сборщиком мусора до завершения.
shield не защищает от task.cancel(), вызванного непосредственно для внутренней задачи. Также он не переживает остановку event loop: при завершении приложения нужно отдельно организовать graceful shutdown и дождаться важных задач.
Сервис принимает платёж и после ответа внешней платёжной системы должен сохранить аудиторскую запись. Если клиент закрыл соединение, веб-фреймворк отменяет задачу обработчика.
Вариант с обычным await прост, но отмена может прервать запись аудита. Полное игнорирование отмены опасно: обработчик может долго удерживать ресурсы и нарушить остановку сервиса. Запуск записи без сохранения задачи тоже ненадёжен: исключение может остаться необработанным, а задача — завершиться при закрытии event loop.
Выбранное решение — создать отдельную задачу, ожидать её через asyncio.shield, после отмены обработчика дождаться её в пределах короткого бюджета времени и затем повторно выбросить CancelledError. Для действительно обязательной фиксации лучше использовать надёжную внешнюю очередь или транзакционное хранилище, потому что shield защищает только от одной формы отмены и не заменяет механизм долговечности данных.
asyncio.shield внутреннюю задачу, если вызывающий код напрямую вызовет для неё cancel()?Ответ: Да, внутренняя задача будет отменена. shield перехватывает только отмену внешнего объекта ожидания и не блокирует прямое управление внутренней задачей. Поэтому доступ к объекту задачи и правила её отмены должны контролироваться отдельно.
asyncio.shield отмену от внешней корутины?Ответ: Нет. Если внешняя задача отменена во время ожидания shield, она получает CancelledError. Отличие состоит в том, что внутренняя задача продолжает выполнение. Если внешняя корутина не обработает исключение, она завершится отменённой, а защищённая задача может закончиться позже.
Ответ: Если тайм-аут применяется к asyncio.shield(task), тайм-аут обычно отменяет защищающий объект ожидания, а не саму task. Внутренняя задача продолжит работу, поэтому её нужно сохранить, дождаться позже или явно отменить, иначе она может стать неконтролируемой фоновой работой. При завершении event loop она не получает гарантии на завершение.