Очереди и журналы

Что это такое

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

Почему это важно

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

Ключевые идеи

Очередь — это работа, которую нужно сделать; журнал — запись о том, что произошло

Сообщение очереди — инструкция, адресованная тому, кто свободен, и жизнь его кончается подтверждением. Запись журнала — факт, дописанный в последовательность, и живёт она до конца срока хранения. Чтобы понять, какая модель подходит, надёжнее прочитать предполагаемый смысл сообщения, чем сравнивать производительность.

очередь: [задача] -> один потребитель -> подтверждено -> исчезла
журнал:  [событие0][событие1][событие2] ... хранится, у каждого потребителя своя позиция

Гарантии порядка — на раздел, а не глобальные

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

Позиция потребителя — состояние, которым нужно управлять

В журнале каждая группа потребителей отслеживает, докуда она дочитала. Если зафиксировать позицию слишком рано, при падении потребителя сообщения потеряются; если слишком поздно — они будут обработаны заново. Обычно позицию фиксируют после того, как работа сделана; значит, доставка дважды возможна, и обработчик обязан быть идемпотентным.

На практике доставка at-least-once, поэтому обработчики должны быть повторяемыми

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

Веерная рассылка естественна в журнале и требует механизма в очереди

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

Срок хранения — параметр проектирования, а не деталь уборки

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

Практика

Выберите модель для двух нагрузок

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

Готово, когда: для каждого выбора названо, требуется ли повторное чтение того же элемента другим потребителем.

Прочитайте одно событие дважды

Опубликуйте сообщение, обработайте его, а затем обработайте снова с сохранённой позиции, ничего не публикуя заново.

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

Частые ошибки

  • Очередь там, где будущему потребителю понадобится история — события потеряны; исправить: используйте журнал, когда запись сама по себе имеет ценность.
  • Ожидание глобального порядка при нескольких потребителях — гарантирован только порядок внутри раздела; исправить: выбирайте ключ так, чтобы связанные сообщения попадали в один раздел.
  • Фиксация позиции до того, как работа сделана — сбой теряет сообщения; исправить: фиксируйте после обработки и сделайте обработчик идемпотентным.
  • Ожидание доставки exactly-once от брокера — повторная доставка нормальна; исправить: сделайте обработчик безопасным для повторного запуска.
  • Срок хранения без учёта восстановления — улики, нужные для исправления ошибки, истекли; исправить: выбирайте срок хранения из окна восстановления.

Что читать дальше