Ключ сообщения Kafka: почему порядок событий клиента нарушился
Сервис заказов отправил события «заказ создан», «оплачен», «отменён». Consumer увидел «оплачен» раньше «создан» и не смог применить переход состояния. Часто в такой ситуации говорят, что Kafka нарушила порядок. Но у Kafka есть более узкое правило: записи читаются в порядке журнала одной партиции. Между разными партициями общего порядка нет.
Если события одного заказа должны обрабатываться последовательно, producer отправляет их с одним и тем же ключом, например order_id=17. При стандартном распределении по ключу такие события направляются в одну партицию темы. Consumer, читающий её, увидит порядок, в котором записи оказались в журнале. Для другого заказа порядок может идти независимо и параллельно.
Как ключ потерялся на пути
Представим три записи: created(order_id=17, key=17), paid(order_id=17, key=customer_8), cancelled(order_id=17, key=17). Средняя запись партиционируется по клиенту, а остальные — по заказу. Даже если payload содержит правильный order_id, Kafka не использует его автоматически: назначение партиции определяет ключ записи, переданный producer. У этих событий уже нет общей гарантии порядка.
Проверьте в логах для одного проблемного заказа topic, partition, offset и фактический key каждой записи. Если партиции отличаются, ищите место, где producer выбрал другой ключ или отправил запись без него. Исправление нужно на стороне публикации, после согласования сущности, чей порядок важен: заказ, клиент или платёж.
Одна партиция не создаёт порядок в источнике
Даже одинаковый ключ не исправляет гонку двух независимых producers. Если сервис A публикует created позже, чем сервис B публикует paid, Kafka честно запишет paid раньше в одну партицию. Для таких потоков нужен единый владелец последовательности либо версия агрегата в событии. Consumer может отклонить переход со старой версией, дождаться отсутствующей версии или отправить событие на разбор согласно контракту.
Версию стоит хранить вместе с идентификатором сущности: order_id=17, version=2. Тогда обработчик знает, что после версии 1 ожидается 2. Timestamp не всегда заменяет версию: часы разных сервисов могут расходиться, а поздняя доставка не означает позднее бизнес-изменение. Версия сообщает логический порядок именно для заказа.
При увеличении числа партиций проверьте, как клиент распределяет ключи. Тот же ключ может попасть в другую партицию после изменения конфигурации, и старые и новые события окажутся в разных журналах. Если строгий порядок на всём протяжении жизни сущности критичен, миграцию темы и стратегию партиционирования нужно проектировать отдельно.
Ключ имеет и цену для производительности. Если почти все события получают один общий ключ вроде shop, они соберутся в одной партиции, а остальные consumer будут простаивать. Слишком мелкий ключ, например случайный id события, хорошо распределит нагрузку, но уничтожит порядок на уровне заказа. Выбирайте ключ по сущности, чей последовательный переход состояния действительно требуется, затем проверьте распределение нагрузки по партициям.
При расследовании важна пара (partition, offset). Offset 15 в партиции 0 нельзя сравнить с offset 15 в партиции 2: это разные журналы. Если бизнес-требование звучит «все события всех заказов строго по времени», многопартиционная тема не даст такой глобальный порядок. Обычно это требование сужают до порядка внутри одного заказа, сохраняя параллельную обработку разных заказов.
После исправления ключа старые сообщения не переедут автоматически. Один заказ может уже иметь историю в разных партициях. Решите, как будут обработаны незавершённые заказы на границе перехода: дождаться вычитки старой темы, записать контрольную версию состояния или создать новую тему с согласованным партиционированием. Иначе свежая корректная публикация столкнётся со старым несогласованным журналом.
В курсе Data Engineering Fast‑Track вы настраиваете Kafka topics, ключи и партиции, затем проводите события через Spark в lakehouse под управлением Airflow. Откройте программу, чтобы связать порядок событий с последующей обработкой и проверками конвейера.
Если записи приходят повторно уже на consumer, разберите момент фиксации offset после обработки.
Продолжить на практике
- Data Engineering Fast‑Track: Kafka → Airflow → Spark ↗
Практический fast-track по data engineering: за 4–6 недель собираем рабочий конвейер Kafka → Spark → Lakehouse под управлением Airflow. Разберём batch и streaming, окна и watermark, схемы и контракты. Мини‑проекты, Docker Compose, шаблоны DAG и пайплайнов. Курс подойдёт инженерам, аналитикам и тем, кто хочет освоить современные пайплайны данных с уверенным Python.
