Код-ревью Senior Kafka в Spring обновлено

Код-ревью: обработка платежей из Kafka

Вопрос

Найдите проблемы в продюсере и потребителе.

@Service
public class PaymentEvents {
    private final KafkaTemplate<String, PaymentEvent> kafka;
    private final PaymentRepository repo;
    private final RestClient bank;

    public PaymentEvents(KafkaTemplate<String, PaymentEvent> kafka,
                         PaymentRepository repo, RestClient bank) {
        this.kafka = kafka;
        this.repo = repo;
        this.bank = bank;
    }

    @Transactional
    public void pay(Payment p) {
        repo.save(p);
        kafka.send("payments", UUID.randomUUID().toString(), new PaymentEvent(p.getId()));
    }

    @KafkaListener(topics = "payments", groupId = "ledger", concurrency = "12")
    public void onPayment(PaymentEvent e) {
        repo.insertLedgerEntry(e.paymentId());
        bank.post().uri("/notify").body(e).retrieve().toBodilessEntity();
    }
}

Коротко

Надёжная интеграция с Kafka держится на трёх вещах: событие публикуется вместе с данными (outbox), ключ сохраняет порядок по сущности, потребитель идемпотентен и имеет обработку ошибок с ограниченными повторами и DLT.

Что должен найти ревьюер

  1. Ошибкастрока 17
    Отправка в Kafka внутри транзакции до коммита: при откате событие уже ушло, а при падении после коммита оно может потеряться. Нужен outbox или отправка после коммита.
  2. Ошибкастрока 17
    Случайный ключ: события одного платежа попадут в разные партиции, порядок не гарантирован. Ключом должен быть id платежа.
  3. Ошибкастрока 17
    Результат send игнорируется: ошибка асинхронной отправки останется незамеченной.
  4. Производительностьстрока 20
    concurrency = 12 бессмысленна, если у топика меньше партиций: лишние потоки простаивают.
  5. Ошибкастрока 22
    Неидемпотентная вставка в журнал: при повторной доставке (at-least-once) запись задвоится. Нужна проверка id сообщения или upsert.
  6. Ошибкастрока 23
    Внешний HTTP-вызов в слушателе без таймаута и обработки ошибок: при сбое банка сообщение ретраится вместе с уже сделанной вставкой, партиция блокируется. Нужен DefaultErrorHandler с DLT и идемпотентность.

Разбор

Надёжная интеграция с Kafka держится на трёх вещах: событие публикуется вместе с данными (outbox), ключ сохраняет порядок по сущности, потребитель идемпотентен и имеет обработку ошибок с ограниченными повторами и DLT.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.

Термины по теме