Почему доставка exactly-once невозможна и что это вообще такое

Читать в полной версии →

Наверняка вы слышали мнение, что доставка exactly-once (или однократная доставка) — невозможна. Это одна из самых сложных систем в сфере распределенных приложений, а ее практическое использование стоит слишком дорого. Существуют и логические ограничения — и сегодня речь пойдет именно о них.

Это адаптированный перевод статьи The impossibility of exactly-once Савваса Клеантоуса, программиста и специалиста по архитектуре CQRS. Повествование ведётся от лица автора оригинала.

Начнем с определения. Доставка exactly-once — это подход, при котором сообщение может быть доставлено получателю строго один раз, без дублей и потери данных. Проблема в том, что в реальном мире гарантировать это невозможно.

Доказательство противопоставлением и две общие проблемы

Мы будем рассматривать проблему на примере задачи про двух генералов — мыслительного эксперимента, суть которого заключается в разработке алгоритма связи между двумя армиями, готовящимися атаковать один и тот же город. Противник находится строго между армиями, а атака только одной армией обречена на провал. Хотя организовать доставку сообщений между ними достаточно просто, гарантировать успех передачи данных — невозможно.

Семантика доставки exactly-once очень похожа на задачу про двух генералов. И в том, и в другом случае есть две стороны, которые пытаются о чем-то сообщить друг-другу. В этом процессе пакеты данных могут потеряться: как два генерала должны согласовать время атаки, так и два процесса должны связаться с друг другом.

Предположим, существует протокол, который гарантирует, что получатель точно получит сообщение — и это произойдет только один раз. Такой протокол мог бы решить проблему двух генералов — если это произойдет, первому генералу (отправителю) для доставки сообщения об атаке нужно будет только придерживаться протокола, чтобы второй генерал (получатель) получил информацию ровно один раз.

Прямое доказательство

Ниже я попытаюсь продемонстрировать, что гарантировать доставку сообщения ровно один, используя метод прямого доказательства, невозможно. Сначала уточним условия:

  1. Отправитель и получатель работают в реальном мире, что означает ненулевое время транспортировки и обработки (и отсутствие строгой согласованности)

  2. Отправитель и получатель не имеют доступа к внутреннему состоянию друг друга. Это означает, что получатель не знает о намерении отправить сообщение, если отправитель об этом не сообщил

  3. Транспортировка сообщений ненадежна

  4. Получатель обязательно должен выполнить обработку сообщения — то есть сообщение нельзя просто удалить или потерять

  5. Операция ограничена по времени: обработка должна быть завершена в течение не бесконечного периода времени.

Пункты один и два исключают существование тривиального решения, которое основывалось бы на проверке внутреннего состояния получателя.

Транспортировка сообщений ненадежна, поэтому отправитель не может заранее знать, что сообщение доставлено: для проверки нужно одно или несколько подтверждающих сообщений. Кроме того, невозможно отличить ошибку доставки или задержки, которая возникает при обработке или из-за ненадежности сети, от глобальной ошибки в доставке. Получатель, в свою очередь, вынужден отправлять ответное сообщение только после обработки (или сохранения для обработки), — то есть подтверждение перед обработкой в данном случае не сработает. Если получатель найдет ошибку перед обработкой, он потеряет сообщение.

Отправитель не имеет доступа к состоянию получателя, поэтому не может знать причину потери сообщения: например, сбой мог произойти в процессе подготовки к отправке, либо получатель просто работает слишком медленно.

Тут уже отправитель встанет перед выбором: продублировать сообщение (и адресат получит его дважды) или не делать этого. Во втором случае получатель может не обработать сообщение, если решить, что проблема возникла в процессе планирования обработки.

Если гарантировать доставку exactly-once невозможно, что делать?

Результат эксперимента, описанного выше, стоит принять во внимание при проектировании распределенных систем. При отсутствии логических ошибок система будет доставлять сообщение только один раз. Но если они возникнут, она будет действовать по одному из двух сценариев:

Доставка at-most-once. Сообщение не доставляется вовсе или доставляется, но теряется до того, как у получателя произойдут какие-либо изменения состояния. В результате получатель останется в том же состоянии, в котором он был до получения сообщения;

Доставка at-least-once. Сообщение доставится как минимум один раз.

Доставка at-most-once

При доставке at-most-once мы либо успешно доставляем сообщение, либо не доставляем его вовсе. Это простая гарантия для реализации и поддержки, поскольку она требует минимальных усилий:

Это означает, что доставка at-most-once — самый простой вариант гарантии доставки как для разработчиков, так и для проектировщиков системы. Если этот подход соответствует условиям задачи, которая перед вами стоит, выбирайте его.

Важно отметить, что в большинстве случаев требуется вести учет потерь при отправке сообщений. Это позволит сообщить о проблеме и даст возможность исправить ее вручную.

Доставка at-least-once

Хотя предыдущий подход проще, на практике чаще применяется доставка at-least-once. Вот несколько причин, почему это происходит:

Доставка at-least-once не добавляет алгоритмической сложности, а для ее внедрения достаточно реализовать следующее:

Часто описанные выше процессы обрабатываются на уровне инфраструктуры, чтобы результат был прозрачен на прикладном и доменном уровнях. Получая сообщение на уровне инфраструктуры, можно преодолеть проблемы интеграции, а затем передать его на уровень приложения, и уже там подтвердить получение.

Доставка без гарантии

Третья и самая простая альтернатива — доставка без гарантии. Проблема с таким подходом в отсутствии согласованности системы.

Deus ex machina: обработка exactly-once

Составьте свое первое резюме: Вы можете бесплатно опубликовать свое резюме в нашем сервисе «Хекслет-CV» и получить советы по его улучшению от разработчиков и HR-менеджеров

Как уже говорилось ранее, единственные подходящие варианты реализации доставки — либо потерять сообщение, либо доставить его более одного раза. В случае с доставкой at-least-once мы сильно рискуем доставить сообщение дважды, что может катастрофические последствия для системы. Неужели мы обречены?

Не совсем: хотя доставка exactly-once невозможна, но ведь обработка exactly-once вполне реализуема. Этот подход гарантирует, что мы обработаем сообщение только один раз — вне зависимости от того, сколько раз оно было отправлено. Реализовать его можно двумя способами:

Никогда не останавливайтесь: В программировании говорят, что нужно постоянно учиться даже для того, чтобы просто находиться на месте. Развивайтесь с нами — на Хекслете есть сотни курсов по разработке на разных языках и технологиях