Idempotent consumers produce the same result whether a message is processed once or multiple times.
Implementation approaches:
Database unique constraint:
INSERT INTO processed_messages (id) VALUES (?) fails if duplicate.
Redis deduplication:
SETNX message_id 1 EX 86400 returns false if already exists.
Idempotent operations:
UPDATE balance = 500 is idempotent. UPDATE balance = balance + 100 is not.
Store message IDs with TTL matching your retry window (typically - hours).