10 KiB
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 |
|
|
|
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 트랜잭션에서 커밋하는 방식을 채택하는 근거, 그리고 "비즈니스 엔티티 자체의 유니크 제약으로 대체" 변형이 언제 성립하는지의 선택 조건 |
출처
- 원본 URL: https://microservices.io/patterns/communication-style/idempotent-consumer.html
- 아카이브 URL: (미등록)
- 저자 / 조직: Chris Richardson — microservices.io (personal pattern catalog). 별도 vendor 의 공식 문서 아님.
- 발행일: rolling docs (페이지에 "Copyright © 2026" 표기, 최초 작성 시점 명시 없음)
- 마지막 확인일: 2026-07-28
왜 저장했는지
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 message’s ID into the PROCESSED_MESSAGE table."
[§Solution] "Since the (subscriberId, messageID) is the PROCESSED_MESSAGE table’s 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 message’s 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 table’s 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 를 생성/갱신하는 경우 (예: AccountDebited → Account 엔티티) |
이 변형을 언제 선택해야 하는지의 판단 기준(예: 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"이라고만 언급, 코드는 미첨부)
- 본 페이지가 공식 vendor doc 이나 표준이라는 점 — microservices.io 는 Chris Richardson 의 personal pattern catalog. 어떤 벤더의 공식 채택도 의미하지 않는다. strength 는 전부
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- ca-skeleton 의
InboxStorePort구현이 (subscriberId, messageID) 복합 PK 와 동등한 유니크 제약을 실제로 갖는지 코드 검증 - inbox insert 와 비즈니스 write 가 실제로 동일
@Transactional경계 안에서 커밋되는지 — 이 raw 자료만으로는 "그래야 한다"는 관례적 근거이지 ca-skeleton 코드의 검증 결과가 아님 - "비즈니스 엔티티 자체에 ID 저장" 변형을 채택할지 여부는 ca-skeleton 의 메시지-엔티티 매핑이 1:1 인 케이스에 한해 별도로 결정해야 함
- ca-skeleton 의
메모
- 본 페이지는 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 양쪽 계약을 함께 볼 때 두 문서를 같이 참조할 것.
Related
- 같은 주제 다른 official-doc:
- 이 자료를 인용하는 branch:
- 이 자료를 인용한 wiki 요약: (미작성)