Зачем это спрашивают
Почти любая система крупнее одного сервиса общается асинхронно: заказ оформили, а письмо, списание со склада и начисление бонусов происходят чуть позже и в других сервисах. Интервьюеру важно понять две вещи. Первая — понимаете ли вы, как события движутся между сервисами, и не путаете ли Kafka с почтовым ящиком, из которого письмо исчезает после прочтения. Вторая — умеете ли вы тестировать то, что не видно в ответе API: результат появляется не сразу и не там, куда вы отправили запрос.
Этот вопрос задают на позициях junior+ и middle, особенно в командах с микросервисами. Глубокого знания администрирования брокера от тестировщика не ждут, а вот модель данных и типовые риски — да.
Как построить сильный ответ
Удобная схема: сначала одна фраза «что это», потом четыре термина, потом риски и что вы проверяете.
Что это
Kafka — распределённый брокер событий. Сервис-продюсер записывает событие и не ждёт, пока его обработают. Сервисы-консьюмеры читают события в своём темпе.
Четыре термина
- Топик — именованный поток однотипных событий, например
orders. - Партиция — часть топика. Топик делят на партиции, чтобы читать параллельно. Порядок событий сохраняется только внутри одной партиции. События с одинаковым ключом (например, номер заказа) попадают в одну партицию, поэтому все события одного заказа идут по порядку.
- Offset — порядковый номер события внутри партиции.
- Группа консьюмеров — экземпляры одного сервиса, которые делят партиции между собой. У каждой группы свой сохранённый offset — номер следующего события к чтению. Поэтому сервис уведомлений и сервис склада читают одни и те же события независимо, не мешая друг другу.
Важная деталь: прочитанное событие из Kafka не удаляется. Оно живёт столько, сколько задано настройкой хранения (retention).
Почему бывают дубли
Обычная настройка — доставка «хотя бы раз» (at least once). Консьюмер обработал событие, но упал до того, как сохранил offset. После перезапуска или перебалансировки группы он прочитает то же событие снова. Защита — идемпотентный консьюмер: он узнаёт повтор по идентификатору события (event_id), который не повторяется у разных событий, или по ограничению UNIQUE в базе и не выполняет действие второй раз. Ключ сообщения для этого не подходит: у одного заказа много разных событий с одинаковым ключом.
Что проверяет тестировщик
- Событие появилось в нужном топике, с правильным ключом и телом (Kafka UI, поиск по ключу, схема сообщения).
- Консьюмеры его обработали: изменились данные в их базах, есть записи в логах.
- Повторная доставка не создаёт дублей.
- Порядок событий одного объекта соблюдается.
- Сообщение с ошибкой уходит в DLQ и не блокирует остальные.
- Лаг консьюмеров (разница между последним offset в партиции и сохранённым offset группы) не растёт бесконтрольно под нагрузкой.
Для проверки результата удобно сделать запрос к базе консьюмера, например в PostgreSQL:
SELECT event_id, count(*)
FROM processed_events
WHERE order_id = 1042
GROUP BY event_id
HAVING count(*) > 1;
Пустой результат значит, что ни одно событие не обработано дважды.
Пример ответа
Kafka — это брокер событий: один сервис пишет событие и идёт дальше, другие читают его в своём темпе. События лежат в топиках, топик разбит на партиции. Порядок сохраняется внутри партиции, поэтому события одного заказа отправляют с ключом-номером заказа. Offset — номер события в партиции. Сервис читает события группой консьюмеров, и у каждой группы свой сохранённый offset, так что склад и уведомления обрабатывают одно событие независимо. После чтения событие не удаляется. Доставка обычно «хотя бы раз»: если консьюмер упал до сохранения offset, событие придёт снова, поэтому консьюмер должен быть идемпотентным и отсеивать повторы по id события. Как тестировщик я проверяю, что событие пришло в нужный топик с правильным телом, что консьюмер его обработал, что повтор не создаёт дубль, что битое сообщение уходит в DLQ и что лаг не копится.
Частые ошибки
- «Это очередь, сообщение удаляется после чтения». Это главный красный флаг: он показывает, что вы не понимаете, как несколько сервисов читают одно событие.
- «Проверяю, что сообщение отправилось». Отправка — половина пути. Ценность в том, что консьюмер правильно обработал событие.
- Объяснять дубли словами «Kafka иногда глючит». Дубли — ожидаемое следствие at least once, и защищаться от них должен консьюмер.
- Считать, что порядок общий для всего топика. Порядок есть только внутри партиции.
- Проверять асинхронный результат сразу после запроса. Нужно ждать с таймаутом и повторной проверкой, иначе тест будет нестабильным — подробнее в статье про flaky-тесты.
Пример на MarketCove
В учебном магазине https://shop.tomiddle.com заказы хранятся в памяти сервера, и Kafka там нет. Но его удобно использовать как мысленную модель. Представьте, что оформление заказа через /api/shop/orders публикует событие order.created с ключом — номером заказа, а на него подписаны два сервиса: письма покупателю и аналитика заказов.
Как бы вы это проверяли:
- оформить заказ и найти событие в топике по ключу: тело совпадает с заказом — номер, товары, статус
PENDING; - убедиться, что письмо отправлено одно, а в аналитике заказ учтён ровно один раз;
- смоделировать повтор: отправить в топик то же событие с тем же
event_idи проверить, что второго письма и второй записи в аналитике нет; - отправить событие с битым телом и убедиться, что оно ушло в DLQ, а следующие заказы обрабатываются;
- отменить заказ сразу после создания и проверить, что события «создан» и «отменён» обработаны в правильном порядке.
Задание для практики
Возьмите любой сценарий из магазина, например «покупатель оформил заказ гостем», и распишите на бумаге: какие события могли бы появиться, какой у каждого ключ, какие группы консьюмеров их читают. Затем составьте пять проверок по списку выше: появление события, обработка, повтор, порядок, ошибка. Для каждой проверки запишите, где именно вы будете смотреть результат — в базе, в логах или в API. Потренируйтесь отвечать вслух за две минуты и сверьтесь с вопросами в разделе про асинхронные системы.
