Files
llm-wiki/raw/official-docs/idempotent-consumer-microservices-io.md
T

10 KiB
Raw Blame History

title, source_type, status, confidence, url, archive_url, related_branches, related_projects, tags, created, last_reviewed
title source_type status confidence url archive_url related_branches related_projects tags created last_reviewed
official-doc / microservices.io — Idempotent Consumer Pattern (Chris Richardson) official-doc raw medium https://microservices.io/patterns/communication-style/idempotent-consumer.html
feature-kafka-consumer-inbox-contract
ca-skeleton
official-doc
ca-skeleton
messaging
kafka
idempotency
2026-07-28 2026-07-28

Idempotent Consumer Pattern — microservices.io (Chris Richardson)

Layer: raw/official-docs/ — Chris Richardson 의 microservices.io 패턴 카탈로그 중 "Idempotent Consumer" 페이지 verbatim 발췌. at-least-once 재전달로 인한 consumer 중복 실행 문제와 processed-message-ID 기록 기반 해결책의 1차 인용 출처.

강도 주의: microservices.io 는 Chris Richardson 개인의 패턴 카탈로그다. raw/official-docs/ 에 두되 strength 는 engineering-blog (개인 패턴 카탈로그) 로 표기한다 — 벤더 공식 표준(official-standard / official-vendor-doc / official-reference)으로 격상 금지. sibling [[raw/official-docs/microservices-io-transactional-outbox]] 와 동일 등급 정책.

Parent / 활용 branch (필수)

Branch 이 자료가 정당화하는 결정
raw/branch-notes/feature-kafka-consumer-inbox-contract ca-skeleton consumer 가 at-least-once 재전달의 중복 실행을 차단하기 위해 처리한 메시지 ID 를 기록하는 inbox(processed-message) 테이블을 비즈니스 write 와 동일 DB 트랜잭션에서 커밋하는 방식을 채택하는 근거, 그리고 "비즈니스 엔티티 자체의 유니크 제약으로 대체" 변형이 언제 성립하는지의 선택 조건

출처

왜 저장했는지

feature-kafka-consumer-inbox-contract branch 가 inbox(PROCESSED_MESSAGE류) 테이블 기반 멱등 consumer 를 채택하는 근거이자, 처리한 메시지 ID 기록을 DB 트랜잭션 경계 안에서 수행해야 duplicate INSERT 가 유니크 제약으로 실패·rollback 되는 메커니즘의 1차 출처. 별도 테이블 vs 비즈니스 엔티티 내장이라는 두 변형 중 어느 쪽을 언제 쓰는지 판단할 근거로 보관한다.

핵심 인용

[§Context] "One side-effect, however, is that the consumer can be invoked repeatedly for the same message."

[§Solution] "Make a consumer idempotent by having it record the IDs of processed messages in the database."

[§Solution] "After starting the database transaction, the message handler inserts the messages ID into the PROCESSED_MESSAGE table."

[§Solution] "Since the (subscriberId, messageID) is the PROCESSED_MESSAGE tables primary key the INSERT will fail if the message has been already processed successfully."

[§Solution] "The other option is for the consumer to store the IDs in the business entities that it creates or updates."

Claims Extracted

Claim ID Claim Evidence quote Strength Applies to Does not prove
MSIO-IDEMPC-C1 at-least-once 전달을 보장하는 메시지 브로커를 쓰면, 부작용으로 consumer 가 동일 메시지에 대해 반복 호출될 수 있다 [§Context] "One side-effect, however, is that the consumer can be invoked repeatedly for the same message." engineering-blog at-least-once delivery 를 보장하는 모든 메시지 브로커(Kafka 포함) 사용 시 재전달 가능성 일반 재전달이 얼마나 자주 발생하는지 정량적 근거 없음; Kafka 고유의 rebalance/재시도·offset 커밋 메커니즘과의 상호작용은 별도 근거 필요
MSIO-IDEMPC-C2 해법의 핵심은 consumer 가 처리한 메시지의 ID 를 DB 에 기록해 멱등성을 확보하는 것 [§Solution] "Make a consumer idempotent by having it record the IDs of processed messages in the database." engineering-blog consumer 가 조회 가능한 저장소(RDB 등)에 접근 가능한 아키텍처 이 저장소가 반드시 별도 RDB 여야 한다는 뜻은 아님 — 다른 저장 매체(예: 분산 KV)의 적합성은 본문에서 다루지 않음
MSIO-IDEMPC-C3 메커니즘: message handler 는 DB 트랜잭션을 시작한 뒤 그 트랜잭션 안에서 메시지 ID 를 PROCESSED_MESSAGE 테이블에 INSERT 한다 [§Solution] "After starting the database transaction, the message handler inserts the messages ID into the PROCESSED_MESSAGE table." engineering-blog ID 기록을 DB 트랜잭션 경계 안에서 수행하는 구현 일반 미발견: 이 문장 자체는 "비즈니스 엔티티 갱신도 같은 트랜잭션에 포함되어야 한다"고 명시적으로 말하지 않는다. 하나의 message handler 가 트랜잭션을 하나만 시작한다는 것에서 강하게 시사될 뿐, "동일 트랜잭션 요구"를 문장으로 직접 진술하지는 않음 — ca-skeleton 결정으로 넘어갈 때 이 gap 을 명시해야 함
MSIO-IDEMPC-C4 중복 탐지 메커니즘은 (subscriberId, messageID) 복합 기본키(primary key) 이며, 이미 처리된 메시지를 다시 INSERT 하면 그 제약 위반으로 실패한다 [§Solution] "Since the (subscriberId, messageID) is the PROCESSED_MESSAGE tables primary key the INSERT will fail if the message has been already processed successfully." engineering-blog RDB 의 기본키/유니크 제약을 dedup 메커니즘으로 쓰는 구현(PostgreSQL 포함) 이 제약 기반 방식이 분산 락이나 애플리케이션 레벨 사전 조회보다 우월하다는 비교 평가는 없음; 유니크 제약이 없는 저장소(순수 NoSQL 등)에는 그대로 적용되지 않음
MSIO-IDEMPC-C5 변형: 별도 PROCESSED_MESSAGES 테이블 대신, consumer 가 생성/갱신하는 비즈니스 엔티티 자체에 메시지 ID 를 저장하는 방식도 가능하다 [§Solution] "The other option is for the consumer to store the IDs in the business entities that it creates or updates." engineering-blog consumer 가 처리마다 정확히 하나의 특정 business entity 를 생성/갱신하는 경우 (예: AccountDebitedAccount 엔티티) 이 변형을 언제 선택해야 하는지의 판단 기준(예: fan-out 메시지, 엔티티가 없는 처리, 여러 엔티티를 건드리는 처리)은 본문에 없음 — 선택 조건은 이 자료만으로 증명되지 않음

Usage Boundaries

  • 이 자료가 직접 증명하는 것:
    • MSIO-IDEMPC-C1: at-least-once 브로커의 재전달 부작용(중복 invocation) 정의
    • MSIO-IDEMPC-C2: 해법의 뼈대 — 처리한 메시지 ID 를 DB 에 기록해 멱등성 확보
    • MSIO-IDEMPC-C3: ID INSERT 가 message handler 의 DB 트랜잭션 안에서 일어난다는 것
    • MSIO-IDEMPC-C4: (subscriberId, messageID) 복합 PK 유니크 제약이 중복 INSERT 를 실패시키는 구체 메커니즘
    • MSIO-IDEMPC-C5: PROCESSED_MESSAGES 별도 테이블의 대안으로 비즈니스 엔티티 자체에 ID 저장이 가능하다는 것(옵션 존재 자체)
  • 이 자료가 증명하지 않는 것:
    • 본 페이지가 공식 vendor doc 이나 표준이라는 점 — microservices.io 는 Chris Richardson 의 personal pattern catalog. 어떤 벤더의 공식 채택도 의미하지 않는다. strength 는 전부 engineering-blog.
    • "메시지 ID 기록과 비즈니스 데이터 갱신이 반드시 같은 트랜잭션이어야 한다"는 명시적 문장C3의 Does not prove 참고. 원문은 트랜잭션이 하나 시작된다는 것만 말하며, 비즈니스 엔티티 갱신이 그 안에 포함된다는 것은 패턴의 일반 관례로 추정될 뿐 이 페이지에서 직접 진술되지 않는다.
    • PROCESSED_MESSAGES 별도 테이블 vs 비즈니스 엔티티 내장 중 어느 쪽이 ca-skeleton 에 더 적합한지의 선택 기준C5의 Does not prove 참고, 본문은 옵션 존재만 언급
    • Kafka 특유의 consumer rebalance / max.poll / manual ack 커밋 시점과 이 패턴의 상호작용
    • Eventuate 프레임워크의 실제 구현 코드 세부(이 페이지는 "implements this pattern"이라고만 언급, 코드는 미첨부)
  • 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
    • ca-skeleton 의 InboxStorePort 구현이 (subscriberId, messageID) 복합 PK 와 동등한 유니크 제약을 실제로 갖는지 코드 검증
    • inbox insert 와 비즈니스 write 가 실제로 동일 @Transactional 경계 안에서 커밋되는지 — 이 raw 자료만으로는 "그래야 한다"는 관례적 근거이지 ca-skeleton 코드의 검증 결과가 아님
    • "비즈니스 엔티티 자체에 ID 저장" 변형을 채택할지 여부는 ca-skeleton 의 메시지-엔티티 매핑이 1:1 인 케이스에 한해 별도로 결정해야 함

메모

  • 본 페이지는 Context / Problem / Solution / See also 4개 섹션으로만 구성된 매우 간결한 패턴 카탈로그 페이지이며, 시퀀스 다이어그램 이미지(/i/IdempotentConsumer/IdempotentConsumer.png) 하나를 포함하지만 alt-text 나 대체 설명 텍스트는 없다.
  • "See also" 에 언급된 Eventuate framework 와 "blog post about this pattern"(/post/microservices/patterns/2020/10/16/idempotent-consumer.html) 은 더 상세한 구현을 담고 있을 가능성이 있음 — 필요 시 별도 raw 로 추가 조사.
  • 동일 저자의 raw/official-docs/microservices-io-transactional-outbox 와 짝을 이루는 패턴(producer 측 outbox ↔ consumer 측 idempotent consumer). ca-skeleton 의 producer/consumer 양쪽 계약을 함께 볼 때 두 문서를 같이 참조할 것.