До 30 ноября

Скидки до 81 000 руб и вторая профессия в подарок!

Главная | Все статьи | Код

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

Время чтения статьи ~6 минут 10
Почему доставка 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 вполне реализуема. Этот подход гарантирует, что мы обработаем сообщение только один раз — вне зависимости от того, сколько раз оно было отправлено. Реализовать его можно двумя способами:

  • Дедупликация: удаление сообщений, если они получены более одного раза
  • Идемпотентная обработка: прием сообщения более одного раза имеет такой же эффект, как и их применение ровно один раз

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

Аватар пользователя Oleg Sabitov
Oleg Sabitov 17 сентября 2021
10
Рекомендуемые программы
профессия
Осваивайте разработку веб-страниц, оживляйте дизайн макетов, публикуйте сайты и приложения. Отслеживайте ошибки в интерфейсе и устраняйте их
10 месяцев
с нуля
Старт 28 ноября
профессия
Обучитесь разработке бэкенда сайтов и веб-приложений — серверной части, которая отвечает за логику и базы данных
10 месяцев
с нуля
Старт 28 ноября
профессия
Выполняйте ручное тестирование веб-приложений, находите ошибки в продукте. Узнайте все о тест-дизайне.
4 месяца
с нуля
Старт 28 ноября
профессия
Научитесь разработке веб-приложений, сайтов и программного обеспечения на языке Java, программируйте и используйте структуры данных
10 месяцев
с нуля
Старт 28 ноября
профессия
новый
Собирайте, анализируйте и интерпретируйте данные, улучшайте бизнес-процессы и продукт компании. Обучитесь работе с библиотеками Python
9 месяцев
с нуля
Старт 28 ноября
профессия
Занимайтесь созданием сайтов, веб-приложений, сервисов и их интеграцией с внутренними бизнес-системами на бекенд-языке PHP
10 месяцев
с нуля
Старт 28 ноября
профессия
Создание веб-приложений со скоростью света
5 месяцев
c опытом
Старт 28 ноября
профессия
Обучитесь разработке визуальной части сайта — фронтенда, а также реализации серверной — бэкенда. Освойте HTML, CSS, JavaScript
16 месяцев
с нуля
Старт 28 ноября
профессия
Разработка бэкенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 28 ноября
профессия
новый
Организовывайте процесс автоматизации тестирования на проекте, обучитесь языку программирования JavaScript, начните управлять процессом тестирования
8 месяцев
c опытом
Старт 28 ноября