Перейти к содержимому

Ordering Guarantees

Границы гарантий порядка Kafka: partition, key, producer, consumer, DLQ и state machine.

Ordering Guarantees в Apache Kafka: порядок сообщений и его границы

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

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

Как Kafka хранит сообщения: топики и партиции

В Kafka топик (topic) — это логическое понятие, которое физически разбито на партиции (partitions). Каждая партиция — это упорядоченная, неизменяемая (append-only) последовательность сообщений. Каждое сообщение внутри партиции получает уникальный последовательный номер — смещение (offset).

Топик "orders" (3 партиции):

Партиция 0:
  offset 0: сообщение A (ключ: order_123)
  offset 1: сообщение B (ключ: order_456)
  offset 2: сообщение C (ключ: order_123)

Партиция 1:
  offset 0: сообщение D (ключ: order_789)
  offset 1: сообщение E (ключ: order_123)

Партиция 2:
  offset 0: сообщение F (ключ: order_456)

Важное следствие: Порядок сообщений гарантируется только внутри каждой партиции. Сообщение C в партиции 0 гарантированно находится после сообщения A и B. Но сообщение E в партиции 1 не имеет определенного порядка относительно сообщения C в партиции 0 — они могут быть прочитаны потребителем в любом порядке в зависимости от того, как потребитель читает из партиций.

Гарантии продюсера: как сообщения попадают в партиции

Продюсер решает, в какую партицию отправить сообщение. Это решение может быть:

1. Явное указание партиции. Продюсер сам знает номер партиции. Редко используется.

2. Через ключ сообщения (key). Стандартный способ. Продюсер вычисляет hash(key) % numberOfPartitions. Один и тот же ключ всегда попадает в одну и ту же партицию. Это гарантирует, что все сообщения с одинаковым ключом будут отправлены в одну партицию и, следовательно, будут упорядочены.

3. Round-robin (key = null). Если ключ не указан, продюсер по умолчанию распределяет сообщения по партициям по очереди (round-robin). Порядок между сообщениями с разными ключами не гарантируется, но даже внутри одного ключа его нет, потому что ключ отсутствует.

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

Решение: Включить параметр max.in.flight.requests.per.connection = 1 (ограничивает количество одновременно передаваемых сообщений) и enable.idempotence = true (гарантирует порядок даже при ретраях). Это стандартная настройка для production.

Гарантии брокера: что происходит внутри

Когда брокер получает сообщение, он:

  1. Проверяет актуальность (если указано).
  2. Записывает сообщение на диск в указанную партицию.
  3. Присваивает смещение (offset) — порядковый номер в этой партиции.
  4. Реплицирует сообщение на реплики (если настроено).

Смещения всегда возрастают внутри партиции. Kafka никогда не меняет порядок сообщений внутри одной партиции после записи.

Гарантия: Если сообщение A было записано в партицию P0 раньше сообщения B, то смещение A будет меньше смещения B. Все потребители увидят A раньше B.

Гарантии потребителя: как читать сообщения, сохраняя порядок

Потребитель может читать из одной или нескольких партиций (в рамках одной consumer group). При чтении из нескольких партиций потребитель получает сообщения из каждой партиции в порядке возрастания смещений, но порядок между партициями не определен.

Пример: Потребитель читает из партиций 0 и 1.

Партиция 0: сообщения A (offset 0), C (offset 1), E (offset 2) Партиция 1: сообщения B (offset 0), D (offset 1)

Потребитель может получить сообщения в порядке: A, B, C, D, E (чередование). Или A, C, E, B, D (сначала все из партиции 0). Никаких гарантий.

Как сохранить порядок на стороне потребителя?

  • Если порядок важен для всех сообщений без исключения — читайте из одной партиции (топик с 1 партицией). Это убивает масштабируемость.
  • Если порядок важен только для сообщений с одним ключом — используйте ключ, и они все попадут в одну партицию. Внутри партиции порядок гарантирован.
  • Если порядок важен для глобальной последовательности (например, все события системы), Kafka не подходит. Нужны другие системы (например, традиционная БД, Apache Pulsar с ordering на уровне топика).

Исключения и нарушения порядка

1. Изменение числа партиций. При увеличении количества партиций в топике отображение hash(key) % partitions меняется. Сообщения с тем же ключом, отправленные до и после изменения, попадут в разные партиции. Порядок между ними не гарантируется.

2. Ретраи без идемпотентности. Если у продюсера enable.idempotence = false и max.in.flight.requests.per.connection > 1, возможна ситуация: первое сообщение потерялось (временный сбой), второе записалось, затем первое переотправилось и записалось после второго. Порядок нарушен.

3. Dead Letter Queue (DLQ). Часто при обработке сообщений с ошибками их отправляют в DLQ. DLQ — отдельный топик. Сообщение из DLQ теряет связь с исходной последовательностью.

4. Перемещение данных между топиками (Kafka Streams, MirrorMaker). При копировании данных в другой топик порядок может быть нарушен, если используется больше одного потока.

Примеры сценариев и требования к порядку

Сценарий 1: Заказы в интернет-магазине.

События одного заказа должны обрабатываться в порядке: created → paid → shipped → delivered.

Решение: ключ = orderId. Допустим, 1000 заказов в минуту. 10 партиций. Каждый заказ попадает в свою партицию, порядок внутри одной партиции гарантирован. Никакая логика не нарушит порядок событий одного заказа.

Сценарий 2: Обновления профиля пользователя.

Последнее обновление должно быть применено после всех предыдущих. Порядок важен.

Решение: ключ = userId. Все обновления одного пользователя идут в одну партицию. Порядок сохранен.

Сценарий 3: Логи веб-сервера.

Порядок не важен. Каждый лог — независимое событие.

Решение: ключ = null (round-robin). Максимальная равномерность, порядок не нужен.

Сценарий 4: Банковские транзакции (дебет и кредит одного счета).

Если дебет (списание) придет раньше кредита (пополнения), логика может ошибочно заблокировать счет. Порядок критичен.

Решение: ключ = accountId. Транзакции одного счета в одну партицию.

Сценарий 5: Материализованные представления (Kafka Streams).

Kafka Streams гарантирует порядок обработки событий с одним ключом в пределах одной задачи (task). Но при изменении числа партиций входного топика или перебалансировке порядок может быть нарушен.

Порядок в Kafka Streams и ksqlDB

Kafka Streams разбивает обработку на задачи (tasks). Каждая задача обрабатывает одну или несколько партиций. Порядок гарантируется внутри одной задачи для сообщений с одинаковым ключом. При перебалансировке (добавление/удаление потока) обработка останавливается, и после перераспределения порядок сохраняется, потому что ключи перераспределяются вместе с партициями.

Важное ограничение: При использовании окон (windowing) и агрегаций, сообщения могут обрабатываться “не в порядке” (out-of-order), если они приходят с опозданием (late arrival). Kafka Streams позволяет настроить допустимое опоздание (allowedLateness).

Как проверить порядок: инструменты и метрики

  • Логи продюсера: Включить отладочный лог для продюсера, чтобы видеть, в какую партицию ушло сообщение.
  • Консольный потребитель: kafka-console-consumer --property print.key=true --property print.partition=true покажет партицию и ключ.
  • Метрики потребителя: records-lag показывает отставание, но не порядок.
  • Сквозные correlation ID: Добавлять в сообщения последовательный номер (sequence number) для проверки порядка на стороне потребителя.

Что делать, если порядок нарушен: диагностика

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

  1. Разные ключи. Проверьте, что все сообщения, которые должны быть упорядочены, имеют одинаковый ключ.
  2. Изменение числа партиций. Если число партиций было увеличено, старые сообщения с одним ключом в старой партиции, новые — в новой. Порядок нарушен.
  3. Ретраи продюсера. Проверьте настройки enable.idempotence и max.in.flight.requests.per.connection.
  4. Клиент с несколькими потоками. Если продюсер отправляет сообщения из нескольких потоков без синхронизации, порядок может нарушиться даже для одного ключа.
  5. Потребитель с конкурентной обработкой. Если потребитель обрабатывает сообщения из одной партиции параллельно (например, пул потоков), порядок внутри партиции не гарантируется.

Резюме

Порядок сообщений в Kafka — это свойство, которое часто неправильно понимают. Запомните главное правило:

Kafka гарантирует порядок сообщений только внутри одной партиции. Между партициями порядка нет.

Как обеспечить порядок:

  • Используйте ключ сообщения, чтобы все связанные сообщения попадали в одну партицию.
  • Убедитесь, что число партиций не меняется (или готовы к нарушению порядка при изменении).
  • Включите enable.idempotence = true и max.in.flight.requests.per.connection = 5 (или меньше) для сохранения порядка при ретраях.
  • Обрабатывайте сообщения в потребителе последовательно (без параллельной обработки внутри одной партиции).

Что не дает порядка:

  • Отсутствие ключа (round-robin).
  • Разные ключи для логически связанных сообщений.
  • Изменение числа партиций.
  • Ретраи без идемпотентности.
  • Параллельная обработка сообщений из одной партиции.

Для аналитика:

При проектировании системы на Kafka всегда определяйте единицу порядка (order key). Это может быть orderId, userId, accountId, deviceId. Документируйте, какие сообщения должны попадать в одну партицию. Понимание границ гарантий порядка Kafka — это разница между системой, которая работает предсказуемо, и системой, которая “иногда” обрабатывает события в неверном порядке, создавая трудноуловимые баги. Kafka дает строгие гарантии, но только в рамках описанных границ. Не пытайтесь требовать от Kafka того, чего она не обещает.

Проверка знаний

Вопрос 1 из 4
Какую гарантию порядка Kafka действительно дает?
Зачем в Kafka используют ключ сообщения (message key), когда важен порядок событий одной сущности?
Почему увеличение числа партиций может нарушить прежний порядок сообщений с одним и тем же ключом?
Что на стороне consumer может разрушить порядок даже внутри одной партиции?

Вопросы, где были ошибки