Ответ
Задача: реализация идемпотентной обработки платежей в распределенной системе заказов с гарантированной доставкой сообщений (Apache Kafka).
Проблема: Сетевые сбои, повторная доставка сообщений от брокера и ретраи клиентов могли привести к двойному списанию средств.
Решение: Комбинация нескольких паттернов:
- Идемпотентность на стороне обработчика: Дедупликация запросов по уникальному
idempotencyKey. - Паттерн Saga: Управление распределенной транзакцией через последовательность событий с компенсирующими действиями (компенсирующие транзакции) на случай ошибок.
- Надежные ретраи: Экспоненциальная backoff-стратегия для повторных попыток.
Пример кода обработчика платежа с идемпотентностью:
@Service
public class PaymentService {
private final Set<String> processedIdempotencyKeys = ConcurrentHashMap.newKeySet();
@Transactional
public void processPayment(Order order, String idempotencyKey) {
// 1. Проверка идемпотентности
if (!processedIdempotencyKeys.add(idempotencyKey)) {
log.info("Платеж для ключа {} уже обработан. Пропускаем.", idempotencyKey);
return;
}
try {
// 2. Основная бизнес-логика
paymentGateway.charge(order.getAmount(), order.getCustomerId());
// 3. Сохранение результата (например, в статус заказа)
orderRepository.updateStatus(order.getId(), OrderStatus.PAID);
} catch (TemporaryFailureException e) {
// 4. При временной ошибке — удаляем ключ для повторной попытки
processedIdempotencyKeys.remove(idempotencyKey);
throw new RetryableException("Ошибка платежа, требуется повтор", e);
}
}
}
Итог: Система стала устойчивой к сбоям сети и повторной доставке сообщений, полностью исключив двойные списания.