Files
llm-wiki/raw/company-tech-blogs/kafka-poison-pill-consumer-stuck-offset-confluent.md

9.3 KiB


company-tech-blog / Spring for Apache Kafka — Beyond the Basics: Can Your Kafka Consumers Handle a Poison Pill?

Layer: raw/company-tech-blogs/ — 외부 자료 원문 발췌·출처 기록. 등급 caveat (필독): Confluent 는 Kafka 상용 벤더이고, 저자(Tim van Baarsen)는 ING Bank 소속 엔지니어로 Confluent 블로그에 기고한 것이다. 이 글은 "타사 프로덕션 장애 사례 보고"라기보다 벤더 소속 블로그에 실린, 벤더 저자의 Spring Kafka 메커니즘 설명에 가깝다. CLAUDE.md §5 에 따라 company-tech-blog 는 사례/관점으로만 취급하며, 이 글만으로 "공식 best practice"라고 격상하지 않는다. Spring Kafka 공식 레퍼런스(reference docs)의 corroboration 없이는 official-vendor-doc strength 를 부여하지 않는다.

Parent / 활용 branch

Branch 이 자료가 정당화하는 결정
raw/branch-notes/feature-kafka-consumer-inbox-contract ca-skeleton consumer 가 역직렬화 실패를 재시도 무의미(non-retryable)로 분류하고 즉시 DLT(회수 경로)로 보내야 하는 근거 — poison pill 이 consumer offset 을 전진시키지 못한 채 무한 재시도 루프에 빠뜨리는 실패 메커니즘, 그리고 역직렬화 실패가 poll() 반환 이전에 발생해 리스너 레벨 예외 처리로는 잡을 수 없다는 사실

출처

왜 저장했는지

ca-skeleton kafka consumer inbox 계약(feature-kafka-consumer-inbox-contract)이 "poison pill(역직렬화 실패)은 재시도 무의미 → 즉시 DLT" 로 분류하려는 결정의 근거로, poison pill 이 consumer 를 어떤 메커니즘으로 멈추게 하는지(offset 미전진 + 무한 재시도)와 그 실패가 Spring 리스너 레벨 예외 처리보다 이른 시점(poll() 반환 전)에 발생한다는 사실을 원문으로 확보하기 위함.

핵심 인용

[§"What is a poison pill?"] "A poison pill (in the context of Kafka) is a record that has been produced to a Kafka topic and always fails when consumed, no matter how many times it is attempted." (line 23 in fetched text)

[§"How can you survive a poison pill scenario?" — ErrorHandlingDeserializer 설명] "When a deserializer fails to deserialize a message, Spring has no way to handle the problem, because it occurs before the poll() returns." (line 113 in fetched text)

[§"What can go wrong if I don't protect my application against poison pills?"] "The consumption of the topic partition is blocked because the consumer offset is not moving forward." (line 89 in fetched text)

[§"What can go wrong if I don't protect my application against poison pills?"] "The consumer will try again and again (very rapidly) to deserialize the record but will never succeed." (line 90 in fetched text)

[§"How can you survive a poison pill scenario?" — 요약] "the poison pill is handled and logged. The consumer offset moves forward so that the consumer can continue consuming the next record." (line 139 in fetched text)

Claims Extracted

Claim ID Claim Evidence quote Strength Applies to Does not prove
CONF-POISON-C1 poison pill 은 Kafka 토픽에 생성된 뒤 몇 번을 시도해도 항상 소비에 실패하는 레코드로 정의된다 "A poison pill (in the context of Kafka) is a record that has been produced to a Kafka topic and always fails when consumed, no matter how many times it is attempted." company-case-study poison pill 용어 정의 일반. 역직렬화 실패를 포함한 "항상 실패하는 레코드" 개념 범위 이 정의가 Kafka 또는 Spring 공식 문서에 등재된 표준 용어라는 것 (벤더 블로그 저자의 서술)
CONF-POISON-C2 역직렬화 실패는 poll() 이 반환되기 이전에 발생하므로 Spring 은 리스너 레벨에서 이 문제를 처리할 방법이 없다 "When a deserializer fails to deserialize a message, Spring has no way to handle the problem, because it occurs before the poll() returns." company-case-study ca-skeleton consumer 가 리스너(listener) 레벨 try/catch 나 애플리케이션 예외 핸들러만으로 역직렬화 실패를 잡을 수 없다는 아키텍처 제약의 근거 모든 Spring Kafka 버전에서 이 메커니즘이 동일하게 유지된다는 것 — 원문은 spring-kafka 2.5.0(2020) 기준 스택 트레이스를 인용하며, 이후 버전 변경 여부는 이 글만으로 확인 불가
CONF-POISON-C3 처리되지 않은 poison pill 은 consumer offset 을 전진시키지 못해 해당 topic partition 의 consumption 을 정지시킨다 "The consumption of the topic partition is blocked because the consumer offset is not moving forward." company-case-study poison pill 미처리 시 offset stuck 실패 메커니즘 일반 auto-commit 과 manual ack 등 커밋 방식 차이에 따라 이 거동이 달라지는지 여부 — 원문은 커밋 방식별 차이를 별도로 분석하지 않음
CONF-POISON-C4 처리되지 않은 poison pill 은 consumer 가 동일 레코드를 매우 빠르게 반복 재시도하며 결코 성공하지 못하는 상태를 만든다 "The consumer will try again and again (very rapidly) to deserialize the record but will never succeed." company-case-study poison pill 미처리 시 무한 재시도 루프 실패 근거 재시도 사이 backoff/interval 이 존재하는지, 로그 볼륨 외의 구체적 리소스 소모 수치 — 원문은 "very rapidly" 로만 서술하고 수치를 제시하지 않음
CONF-POISON-C5 ErrorHandlingDeserializer 를 구성하면 poison pill 이 처리·로깅되고 consumer offset 이 전진해 다음 레코드 소비를 계속할 수 있다 "the poison pill is handled and logged. The consumer offset moves forward so that the consumer can continue consuming the next record." company-case-study ca-skeleton 이 역직렬화 실패 레코드를 격리(DLT 등)하고 offset 을 커밋해야 한다는 결정의 방향성 근거 ErrorHandlingDeserializer 가 ca-skeleton 이 채택할 구체적 DLT 라우팅 메커니즘(retry topic, replay 정책 등)의 유일한 구현 방법이라는 것 — 원문은 Spring Kafka 의 한 가지 구성 예시만 보여줌

Usage Boundaries

  • 이 자료가 직접 증명하는 것:
    • CONF-POISON-C1: poison pill 의 정의(항상 실패하는 레코드)
    • CONF-POISON-C2: 역직렬화 실패가 poll() 반환 이전에 발생해 Spring 리스너 레벨 예외 처리로 잡을 수 없다는 메커니즘
    • CONF-POISON-C3, CONF-POISON-C4: 처리되지 않은 poison pill 이 offset 미전진 + 무한 재시도로 이어지는 실패 사슬
    • CONF-POISON-C5: ErrorHandlingDeserializer 적용 시 offset 이 전진해 poison pill 을 우회할 수 있다는 방향성
  • 이 자료가 증명하지 않는 것:
    • Kafka 또는 Spring 공식 문서가 "역직렬화 실패는 항상 DLT 로 보내야 한다"고 명시한다는 것 — 이 글은 company-tech-blog(벤더 사례)이며 공식 best practice 문서가 아니다 (CLAUDE.md §5)
    • ca-skeleton 이 채택할 구체적 DLT 토픽 명명, retry 정책, replay 감사 메커니즘 — 이 글은 Spring Kafka 의 ErrorHandlingDeserializer 구성 예시 하나만 제시
    • 최신 Spring Kafka 버전(2020년 이후)에서도 동일한 poll() 이전 실패 메커니즘이 유지된다는 것 — 버전별 재검증 필요
  • 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
    • ca-skeleton 이 실제 사용할 Spring Kafka / spring-boot 버전에서 ErrorHandlingDeserializerDefaultErrorHandler(또는 후속 API)의 현재 동작이 이 글의 서술과 동일한지 공식 Spring Kafka 레퍼런스로 재확인
    • manual acknowledgement 모드에서 poison pill 발생 시 offset 커밋 시점이 이 글의 auto-commit 전제와 동일한지 확인 (branch 범위: manual ack 채택 예정)

메모

  • 인용 1 해석 후보 (미검증): "poison pill" 은 Kafka 생태계에서 널리 쓰이는 은유적 표현으로 보이나, 이 글 자체가 그 유래를 규명하지는 않음 — RFC/공식 용어집 등재 여부는 별도 확인 필요.
  • 추가로 봐야 할 동일 출처 페이지: Spring Kafka 공식 레퍼런스의 ErrorHandlingDeserializer / DefaultErrorHandler / DeadLetterPublishingRecoverer 섹션 (official-vendor-doc corroboration 후보).

관련

  • 같은 주제 다른 official-doc / company-tech-blog: (아직 없음 — Spring Kafka 공식 레퍼런스 등록 시 여기 추가)
  • 이 자료를 인용한 wiki 요약: [[wiki/concepts/...]] (생성 시)