Deadline Propagation

Знакома ситуация: переключаете нагрузку на развернутый сервис, а он ложится, и так по кругу? Почему-то в контексте распределённых систем про Deadline Propagation говорят намного реже, чем должны. Сегодня я намерен исправить эту несправедливость.

Контекст

Имеется цепочка сетевых вызовов:

Client -> A -> B -> C -> ...

Вполне возможно, что клиент перестанет ждать ответа от такой системы раньше, чем она завершит свою работу. Предположим, что это случилось в момент, когда обработка находилась на этапе обработки на узле B:

Client -❌-> A -> *B* -> C -> ...

В этом случае цепочка вычислений не прервется: узел B отработает и вызовет C и т.д. И только в момент, когда узел A попытается отправить ответ клиенту, возникнет ошибка.

Ситуация возможна в любой распределённой системе, в особенности, когда у клиента есть жесткий таймаут на получение ответа.

Проблема

Начиная с момента прерывания со стороны клиента, вся остальная цепочка вычислений становится бесполезной нагрузкой на систему и инфраструктуру. Хуже всего то, что после получения таймаута клиент может попытаться сделать retry и усугубит ситуацию, т.к. к уже существующей нагрузке добавится новая (load amplification).

Можно попытаться отменить вычисления, используя Cancellation Propagation, но этот подход имеет ряд существенных недостатков:

  • Сложность реализации. Каждый узел в цепочке должен реализовать механизм отмены вычислений. Таким образом, проблема, которая должна решаться на инфраструктурном уровне, начинает решаться на прикладном.
  • Cancel-запрос отправляется вдогонку. Во-первых, это еще один запрос, который нужно обработать. Если система уже нагружена, cancel-запросы только ухудшат ситуацию. Во-вторых, если cancel придет поздно, бесполезная работа уже будет сделана.
  • Cancel-запрос может не достичь цели. Например, если cancel добежал до B, но между B и C нет сетевой связности, всё напрасно: C и вся остальная цепочка продолжит выполнять бесполезную работу.

Решение

В каждом запросе клиент передает Deadline выполнения, после которого производит разрыв соединения. Значение Deadline передается вниз по цепочке. Каждый узел производит проверку, что он успевает выполнить свою работу или прерывает исполнение, если время уже вышло.

Значение Deadline может быть:

  • Абсолютным (deadline timestamp) – точная дата и время, когда истекает срок. Преимущество – время работы захватывает сетевые издержки и возможные паузы (VM Pauses, GC Stop-the-World), а не только чистое время работы на узле. Это может быть особенно полезно, если клиенту важней контролировать общую продолжительность по wall clock, а не время вычислений. Недостаток – часы на узлах должны быть хорошо синхронизированы, ведь deadline timestamp сравнивается с текущим временем на узле исполнения. Если все узлы работают в общей инфраструктуре, то проблема решается синхронизацией через NTP.
  • Относительным (execution timeout) – сколько осталось время для оставшейся работы. Преимущество – не нужна строгая синхронизация часов. Недостаток – не учитываются сетевые издержки и возможные паузы. Этой концепции придерживается, например, gRPC. Решение допустимо для инфраструктуры, где издержки на вызовы минимальны; и наоборот, если нельзя допускать, чтобы запросы падали по Deadline всякий раз, когда инфраструктура немного лагает.

Никто не запрещает передавать и абсолютный, и относительный Deadline, комбинируя оба подхода.

Способ передачи Deadline зависит от транспорта. Например, в gRPC этот механизм встроен в протокол; в HTTP – заголовки; в Kafka/AMQP и т.д. также есть концепция заголовков.

Плюсы

  • Система перестает делать бесполезную работу.
  • Нет амплификации нагрузки.
  • Узлы работают автономно и сами принимают решение о прекращении.
  • Логика обработки унифицирована и находится на инфраструктурном уровне.

Минусы

  • Короткий Deadline может провоцировать нежелательные прерывания.
  • Для большинства протоколов инфраструктурный код придётся писать самому.


Понравилась статья?

Посмею напомнить, что у меня есть Telegram-канал Архитектоника в ИТ, где я публикую материал на похожие темы примерно раз в неделю. Подписчики меня мотивируют, но ещё больше мотивируют живые дискуссии, ведь именно в них рождается истина. Поэтому подписывайтесь на канал и будем оставаться на связи! ;-)

Статьи из той же категории: