position() vs committed() — '어디까지 읽었나'와 '어디까지 확정됐나'는 다른 값이다
Kafka sendOffsetsToTransaction 및 Consumer position()/committed() 공식 문서 종합 (웹 리서치, 2026) — Apache Kafka KIP-98(Exactly Once Delivery and Transactional Messaging), Apache Kafka 공식 Javadoc KafkaConsumer Apache Kafka 공식 Javadoc KafkaConsumer
이 모듈을 다 읽으면
- position()이 로컬 메모리상의 다음 읽을 오프셋이고, committed()가 실제로 Kafka에 저장된 오프셋이라는 차이를 설명할 수 있다
- auto-commit이 꺼져 있을 때 이 두 값이 왜 서로 다른 시점에 갱신되는지 설명할 수 있다
kafka-learn-08에서 다룬 오프셋 커밋 전략을 전제로, 컨슈머가 실제로 노출하는 두 종류의 오프셋 조회 API(position()과 committed())가 서로 다른 것을 가리킨다는 것을 정리한다.
position() — 로컬 메모리 안의 '다음에 받을 오프셋'
position()이 반환하는 값은 그 파티션에서 poll()로 다음번에 받게 될 레코드의 오프셋이다 — 즉 지금까지 그 컨슈머가 본 가장 높은 오프셋보다 1 큰 값이다. 이 값은 poll() 호출로 메시지를 받을 때마다 자동으로 전진하는, 컨슈머 프로세스의 로컬 메모리 안에 있는 값이다. Kafka 서버에 저장되는 값이 아니라, 이 컨슈머 인스턴스가 지금 어디까지 읽었는지를 그 순간 프로세스 안에서만 추적하는 값이라는 게 핵심이다.
핵심 포인트
- position(): 그 파티션에서 다음 poll()로 받을 오프셋(지금까지 본 가장 높은 오프셋+1) — poll()마다 자동 전진
- 컨슈머 프로세스의 로컬 메모리 안 값 — Kafka 서버에 저장되는 값이 아님
committed() — 실제로 Kafka에 안전하게 저장된 오프셋
committed()가 반환하는 값은 이와 다르다 — 실제로 안전하게(Kafka의 오프셋 저장소에) 저장된 마지막 오프셋이다. 프로세스가 죽었다가 재시작하면, 컨슈머는 바로 이 committed 오프셋부터 다시 읽기 시작한다. auto-commit이 꺼져 있는 경우, 컨슈머가 poll()로 메시지를 여러 개 읽어 position()이 계속 전진해도, 개발자가 명시적으로 commitSync()/commitAsync()를 호출하기 전까지는 committed() 값이 그대로 멈춰 있다 — 즉 '내가 읽은 것'과 'Kafka 관점에서 소비된 것으로 확정된 것'을 개발자가 원하는 시점에 분리해서 통제할 수 있다는 뜻이다.
핵심 포인트
- committed(): Kafka 오프셋 저장소에 실제로 저장된 마지막 오프셋 — 프로세스 재시작 시 이 지점부터 재개
- auto-commit 꺼져 있으면 position()은 poll()마다 전진해도 commitSync()/commitAsync() 호출 전까지 committed()는 그대로 — '읽은 것'과 'Kafka 관점의 확정된 소비'를 원하는 시점에 분리 통제 가능