Files
llm-wiki/raw/official-docs/spring-kafka-ack-mode-manual-commit-and-concurrency.md

16 KiB

title, source_type, url, archive_url, related_branches, related_projects, tags, created
title source_type url archive_url related_branches related_projects tags created
official-doc / Spring for Apache Kafka — Message Listener Containers (AckMode & Concurrency) official-doc https://docs.spring.io/spring-kafka/reference/kafka/receiving-messages/message-listener-container.html
feature-kafka-consumer-inbox-contract
ca-skeleton
official-doc
ca-skeleton
messaging
kafka
2026-07-28

official-doc / Spring for Apache Kafka — Message Listener Containers (AckMode & Concurrency)

Layer: raw/ — 외부 자료(공식 문서)의 원문 발췌·출처 기록. 검증된 요약은 /ingestwiki/concepts/에 별도 작성. 원본은 raw에 영구 보관.

Parent / 활용 branch

Branch 이 자료가 정당화하는 결정
raw/branch-notes/feature-kafka-consumer-inbox-contract manual acknowledgement 메커니즘(D3 — "application use case 성공 + DB 트랜잭션 커밋 이후에만 ack")을 ContainerProperties.AckMode 층에서 무엇으로 표현하는지의 근거. AckMode.MANUAL/MANUAL_IMMEDIATE 정의·Acknowledgment 호출 제약(nack() 스레드 제약, acknowledge(index) 리스너 스레드 제약)·ConcurrentMessageListenerContainerconcurrency 대 파티션 수 하향 조정 규칙을 확인

출처

  • 원본 URL: https://docs.spring.io/spring-kafka/reference/kafka/receiving-messages/message-listener-container.html
  • 아카이브 URL: (미제공)
  • 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka reference)
  • 문서 버전: Spring for Apache Kafka reference 4.1.0 (페이지 상단 breadcrumb "Spring for Apache Kafka 4.1.0" 확인). 페이지 자체의 별도 발행일 표기는 없고 각 기능 옆에 도입 버전만 표기됨(예: AckMode 관련 enable.auto.commit 강제 false 는 2.3부터, acknowledge(index) 부분 배치 커밋은 3.0.10부터).
  • 발행일: 명시 없음 (버전 이력만 본문에 표기)
  • 마지막 확인일: 2026-07-28

왜 저장했는지

ca-skeleton kafka consumer inbox 브랜치(feature-kafka-consumer-inbox-contract)의 D3(수동 ack, DB 커밋 이후에만 offset 커밋) 결정을 Spring Kafka 컨테이너 API 레벨의 AckMode 값과 Acknowledgment 인터페이스 계약으로 정당화하기 위함. 아울러 ConcurrentMessageListenerContainerconcurrency 가 파티션 수를 넘길 때 어떻게 처리되는지(D4/D6 의 "파티션당 컨슈머 1개" 전제와 인접)도 이 페이지에서 확인.

요청받은 5개 확인 대상 중 이 URL 페이지에 실제로 있는 것은 2개(AckMode 열거·MANUAL/MANUAL_IMMEDIATE 정의, concurrency 하향 조정)뿐이다. 나머지 3개(① Acknowledgment 를 어느 스레드에서 호출해야 하는지의 "calling consumer thread ... otherwise queued" 계열 문장, ② ack 순서 제약 "must be acknowledged in order ... does not maintain state for each record" 계열 문장, ③ asyncAcks/out-of-order ack 의 pause·중복 전달 trade-off 문장)은 self-grep 결과 이 페이지에 없음을 확인했다(아래 ## 메모 참조). 1 dispatch = 1 URL 원칙에 따라 다른 페이지 내용을 끌어와 대신 인용하지 않았다.

핵심 인용

원문 그대로. 이 페이지는 "Message Listener Containers" 단일 페이지이며 그 안에 "Committing Offsets" 소제목이 있다. 하위 번호 섹션이 없어 위치는 fetched text 의 line 번호로 표기한다 (self-grep 참조).

[Committing Offsets] "MANUAL: The message listener is responsible to acknowledge() the Acknowledgment." (line 447)

[Committing Offsets] "MANUAL_IMMEDIATE: Commit the offset immediately when the Acknowledgment.acknowledge() method is called by the listener." (line 450)

[Committing Offsets] "MANUAL and MANUAL_IMMEDIATE require the listener to be an AcknowledgingMessageListener or a BatchAcknowledgingMessageListener." (line 454)

[Committing Offsets] "The default AckMode is BATCH." (line 429)

[Committing Offsets] "nack() can only be called on the consumer thread that invokes your listener." (line 480)

[Committing Offsets — partial batch commit] "The method must be called on the listener thread" (line 507, acknowledge(index) 제약 목록 중 하나)

[Using ConcurrentMessageListenerContainer] "If the concurrency is greater than the number of TopicPartitions, the concurrency is adjusted down such that each container gets one partition." (line 412)

Claims Extracted

Claim ID Claim Evidence quote Strength Applies to Does not prove
SPRK-ACKMODE-C1 AckMode.MANUAL 은 리스너가 Acknowledgment.acknowledge() 를 호출할 책임을 지며, 그 이후엔 BATCH 와 동일한 커밋 시맨틱(poll() 이 반환한 레코드 전체 처리 후 커밋)이 적용된다 "MANUAL: The message listener is responsible to acknowledge() the Acknowledgment." + (같은 항목의 다음 문장, self-grep 대상은 아니나 문맥) "After that, the same semantics as BATCH are applied." official-vendor-doc ca-skeleton D3 의 "use case 성공 이후에만 ack" 를 표현할 AckMode 값이 MANUAL 임을 확인 acknowledge() 를 언제 호출해야 하는지(DB 커밋 성공 이후)는 애플리케이션 책임이지 프레임워크가 강제하지 않음 — 순서를 지키는 코드는 리뷰/계약 테스트로 별도 보장해야 함(branch note 의 §검증해야 할 주장 이미 인지)
SPRK-ACKMODE-C2 AckMode.MANUAL_IMMEDIATEAcknowledgment.acknowledge() 호출 즉시 offset 을 커밋한다 — MANUAL 의 배치형 커밋과 달리 호출마다 즉시 커밋 "MANUAL_IMMEDIATE: Commit the offset immediately when the Acknowledgment.acknowledge() method is called by the listener." official-vendor-doc MANUAL vs MANUAL_IMMEDIATE 선택 — ca-skeleton D3 는 "커밋 시점"만 결정했고 이 자료는 두 모드가 커밋 빈도(배치 대 즉시)에서 갈린다는 것을 보여줄 뿐, 어느 쪽이 D3 의 의도에 더 맞는지는 이 자료만으로 결정 불가 두 모드의 처리량/지연 trade-off (벤치마크 없음). acknowledge(index) 부분 배치 커밋(SPRK-ACKMODE-C6)은 MANUAL_IMMEDIATE 를 요구한다는 점에서 이 claim 과 연결되나 별도 claim으로 분리
SPRK-ACKMODE-C3 MANUAL/MANUAL_IMMEDIATE 를 쓰려면 리스너가 AcknowledgingMessageListener 또는 BatchAcknowledgingMessageListener 를 구현해야 한다 "MANUAL and MANUAL_IMMEDIATE require the listener to be an AcknowledgingMessageListener or a BatchAcknowledgingMessageListener." official-vendor-doc ca-skeleton consumer 구현 시 리스너 인터페이스 선택 제약 — Acknowledgment 를 받으려면 이 인터페이스 중 하나를 구현해야 함(구체 클래스/메서드 명명 자체는 이 자료 범위 밖, §구현 가이드 1 의 UNSUPPORTED_IMPL_DECISION 대상) 어느 인터페이스(단건 vs 배치)를 골라야 하는지는 D4 의 처리 단위 결정에 종속 — 이 자료는 존재 요건만 말함
SPRK-ACKMODE-C4 AckMode 를 명시하지 않으면 기본값은 BATCH 이지 MANUAL 이 아니다 "The default AckMode is BATCH." official-vendor-doc ca-skeleton 이 D3 의 수동 ack 계약을 실제로 적용하려면 AckMode명시적으로 MANUAL (또는 MANUAL_IMMEDIATE) 로 설정해야 함 — 설정을 빠뜨리면 조용히 BATCH(자동 그룹 커밋)로 동작 BATCH 자체가 언제 위험한지(자동 커밋과 유사한 문제)는 이 자료가 직접 논하지 않음 — 이미 수집한 kafka-consumer-offset-commit-semantics-apache-javadoc 의 자동 커밋 위험 논의가 별도 근거
SPRK-ACKMODE-C5 nack()(2.3 부터 추가된 Acknowledgment 메서드)은 리스너를 호출한 그 consumer 스레드에서만 호출할 수 있다 "nack() can only be called on the consumer thread that invokes your listener." official-vendor-doc Acknowledgment 인터페이스의 적어도 한 메서드(nack)가 리스너/consumer 스레드에 강하게 묶여 있다는 것을 확인 — "별도 워커 스레드에서 ack 관련 호출을 해도 되는가"라는 branch 의 질문에 nack() 에 한해서는 "아니오" 라고 명시적으로 답함 acknowledge() 자체(단건 리스너, MANUAL 모드)에 동일한 스레드 제약이 있다고 이 문장만으로 단정할 수 없다 — 이 페이지가 acknowledge() 에 대해 그런 문장을 쓰는 곳은 SPRK-ACKMODE-C6(부분 배치 커밋 변형)뿐이며, 그건 다른 API(acknowledge(index), 배치 리스너, MANUAL_IMMEDIATE 한정)다. 사용자가 원한 "calling consumer thread ... otherwise queued" 문장은 이 페이지에 없음(미발견, 아래 Usage Boundaries)
SPRK-ACKMODE-C6 3.0.10 부터 추가된 배치 리스너의 부분 배치 커밋(acknowledge(index))은 AckMode.MANUAL_IMMEDIATE 를 요구하고, "리스너 스레드에서 호출되어야 한다" 는 제약을 명시적으로 건다(그 외에도 List 소비 요건, index 범위·단조 증가 요건이 있으며 위반 시 IllegalArgumentException/IllegalStateException) "The method must be called on the listener thread" (같은 목록의 다른 제약: "AckMode.MANUAL_IMMEDIATE is required") official-vendor-doc acknowledge(index) 부분 배치 커밋을 쓸 경우의 스레드 제약 — 이 자료가 제공하는 가장 근접한 "Acknowledgment 관련 호출은 리스너/consumer 스레드에서" 근거 단건 레코드 리스너의 일반 acknowledge() 호출(MANUAL 모드, 배치 아님)에도 동일 제약이 명시돼 있는지는 이 페이지에서 확인 불가 — 그 문장은 별도 페이지("Manually Committing Offsets")에 있을 가능성이 있으나 본 dispatch 범위 밖
SPRK-ACKMODE-C7 ConcurrentMessageListenerContainerconcurrency 가 배정 가능한 TopicPartition 수보다 크면, 각 delegate 컨테이너가 파티션 하나씩만 갖도록 concurrency 가 하향 조정된다 "If the concurrency is greater than the number of TopicPartitions, the concurrency is adjusted down such that each container gets one partition." official-vendor-doc ca-skeleton 의 concurrency 설정 상한 근거 — 파티션 수를 넘는 concurrency 값은 무의미(초과분 컨테이너가 유휴 상태로 남지 않고 애초에 만들어지지 않음). D4/D6 이 전제하는 "파티션 하나는 컨슈머 그룹 안에서 한 컨테이너만 소비"라는 가정과 정합되는 Spring 프레임워크 레벨 동작 이 문장은 Spring 컨테이너의 파티션 분배 동작을 말할 뿐, Kafka 프로토콜 레벨에서 "파티션 하나는 그룹 내 정확히 한 consumer 가 소비한다"는 것 자체의 공식 Kafka 문서 근거(branch SOURCE_GAP-1, needs-confirmation)를 대신하지 않는다 — Spring 이 이렇게 동작하는 것은 그 Kafka 레벨 보장을 전제로 구현했을 뿐, 이 문서가 Kafka 프로토콜 자체를 규정하는 문서는 아님

Strength 참고

모든 claim 은 official-vendor-doc (Spring 공식 reference — RFC/IETF 표준은 아니므로 official-standard 로 격상하지 않음).

Usage Boundaries

  • 이 자료가 직접 증명하는 것:
    • SPRK-ACKMODE-C1~C4: AckMode 값의 열거와 커밋 시점 정의, 기본값이 BATCH 라는 것, MANUAL/MANUAL_IMMEDIATE 사용 시 리스너 인터페이스 요건
    • SPRK-ACKMODE-C5: nack() 은 consumer/리스너 스레드에서만 호출 가능
    • SPRK-ACKMODE-C6: acknowledge(index)(부분 배치 커밋, MANUAL_IMMEDIATE 한정)는 리스너 스레드에서 호출돼야 한다는 명시 제약
    • SPRK-ACKMODE-C7: concurrency > 파티션 수일 때 하향 조정
  • 이 자료가 증명하지 않는 것 (branch 가 요청했으나 이 URL 페이지에서 미발견 — self-grep 결과 0건):
    • Acknowledgment.acknowledge()(단건 레코드 리스너, MANUAL 모드) 를 어느 스레드에서 호출해야 하는지에 대한 일반 규칙 — 사용자가 지정한 "commit will be performed immediately if the Acknowledgment is acknowledged on the calling consumer thread; otherwise, the acks will be queued" 계열 문장은 grep -nF로 "calling consumer thread"/"queued" 검색 시 이 페이지에서 0건. nack()(C5)과 acknowledge(index)(C6)에 대해서만 스레드 제약이 명시돼 있고, 일반 acknowledge() 에 대한 동일 문장은 이 페이지 범위 밖(다른 페이지 "Manually Committing Offsets" 가능성 — 본 dispatch 는 여기서 멈춤, 1 dispatch = 1 URL)
    • ack 순서 제약 — "acknowledgments must be acknowledged in order, because Kafka does not maintain state for each record, only a committed offset for each group/partition" 계열 문장 — grep -nF로 "does not maintain state"/"in order" 검색 시 미발견(단, "Out of Order Commits" 라는 기능명 자체는 이 페이지에 1회 등장 — line 482, 483: nack() 은 Out of Order Commits 사용 시 허용 안 됨. 이 기능의 정의 문장 자체는 이 페이지에 없음)
    • asyncAcks/out-of-order ack 채택 시 consumer 가 pause 되고 중복 전달 가능성이 커진다는 trade-off 문장 — grep -ni "asyncAck" 결과 0건
    • 파티션 단위 pause/resume API — 이미 별도 raw 문서(raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure SPRK-PAUSE-C5)에서 동일하게 미발견 확인됨
    • commitSync/commitAsync 중 어느 것을 쓸지의 권고 (이 페이지는 syncCommits 컨테이너 프로퍼티가 그 둘을 스위칭한다는 것만 언급하고 권고는 안 함 — 이미 branch D3 의 Open Risk 로 기록됨)
  • 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
    • Acknowledgment.acknowledge() 의 일반적 스레드 제약(있다면)을 확인하려면 "Manually Committing Offsets" 페이지(Message Listeners 섹션의 sibling 페이지, 이 문서 최하단 nav 에서 확인됨)를 별도 wiki-source-summarizer dispatch 로 조사해야 한다
    • ack 순서 제약과 asyncAcks trade-off 도 마찬가지로 별도 페이지 조사 필요 — 이 페이지에 없다고 해서 Spring Kafka 문서 전체에 없다는 뜻은 아님

메모

나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.

  • self-grep 실행 결과 (fetched text /tmp/.../scratchpad/source-fetch-20260728-193000.txt, 658줄):
    • calling consumer thread → 0건
    • queued → 0건
    • does not maintain state → 0건
    • asyncAck (대소문자 무관) → 0건
    • Out of Order Commits → 1건 (line 482, 기능명만 등장 — 정의 문장 없음)
  • 이 페이지 자체가 "Committing Offsets" 절 안에서 AckMode 7종(RECORD/BATCH/TIME/COUNT/COUNT_TIME/MANUAL/MANUAL_IMMEDIATE)을 모두 정의하지만, 이 raw 문서에는 branch 의 관심사(MANUAL/MANUAL_IMMEDIATE 대비, 스레드, concurrency)에 직접 관련된 것만 발췌했다. RECORD/BATCH/TIME/COUNT/COUNT_TIME 의 정의 자체도 필요해지면 이 문서에 claim 추가만으로 확장 가능(재-fetch 불필요, 같은 텍스트 파일에 이미 있음).
  • 추가로 봐야 할 동일 출처 페이지: 사이트 nav 에서 확인된 "Manually Committing Offsets" (이 페이지 바로 다음 sibling, Message Listeners 섹션 하위) — Acknowledgment.acknowledge() 스레드 규칙과 ack 순서 제약이 있을 가능성이 높음. 별도 URL, 별도 dispatch 필요.

관련

  • 같은 주제 다른 official-doc: [[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]] (같은 "Message Listener Containers" 상위 카테고리, pause/resume 근거), [[raw/official-docs/kafka-consumer-offset-commit-semantics-apache-javadoc]] (Kafka 레벨 offset commit 시맨틱), [[raw/official-docs/kafka-incremental-cooperative-rebalance-kip429]] (concurrency/rebalance 인접 주제)
  • 이 자료를 인용한 wiki 요약: [[wiki/concepts/...]] (생성 시)