API / Microservices Design Patterns Interview Questions
What is the Idempotent Consumer pattern and why is it essential in event-driven systems?
The Idempotent Consumer pattern ensures that processing the same message more than once produces the same outcome as processing it exactly once. It is essential because virtually all message brokers (Kafka, RabbitMQ, SQS) guarantee at-least-once delivery — a message may be redelivered after a consumer crashes before acknowledging, after a network partition, or during broker rebalancing. Without idempotency, redelivery causes duplicate side effects: double charges, duplicate shipments, over-reserved inventory.
// Idempotent consumer using deduplication table public void handleOrderPlaced(OrderPlacedEvent event) { String msgId = event.getMessageId(); // unique per message if (processedMessages.exists(msgId)) { log.info("Duplicate message {}, skipping", msgId); return; // idempotency guard: already processed } // process inside a transaction that also inserts the msgId transactionTemplate.execute(status -> { orderRepository.createFrom(event); processedMessages.insert(msgId, Instant.now()); return null; }); }
Implementation strategies:
- Deduplication table — persist message IDs (or idempotency keys) in a table. Before processing, check if the ID exists. Insert the ID and process in the same transaction so a crash between processing and acknowledging still results in a consistent state on retry.
- Natural idempotency — design operations that are inherently idempotent.
UPDATE orders SET status='CONFIRMED' WHERE id=42is idempotent; running it twice has no extra effect. ButINSERT INTO charges (amount, orderId) VALUES (59.99, 42)is not — it creates duplicate rows. - Conditional update — use an optimistic locking version or a state machine check (
WHERE status='PENDING') to ensure the operation only applies in the correct state, making reprocessing a no-op if the state has already advanced.
The deduplication store must be co-located or transactionally integrated with the main data store, otherwise the check-then-insert itself has a race condition.
More Related questions...