- Kafka 호환 스트리밍 시스템인 Bufstream 0.1.0~0.1.3 검증에서 Bufstream 자체의 가용성 문제 2건과 안전성 문제 3건이 발견됐고, 0.1.3 기준으로 5건 모두 수정됨
- 테스트는 Java Kafka Client 3.8.0과 기존 Kafka/Redpanda Jepsen 테스트를 바탕으로 했으며,
acks = all,enable.idempotence = true,enable.auto.commit = false,read_committed등 안전성 우선 설정을 사용함 - Bufstream 문제는 소비자·생산자 정지, 잘못된 offset
0응답, 트랜잭션 커밋 손실, fetch API 응답 크기 필터링 버그로 인한 인정된 쓰기 손실을 포함함 - 조사 과정에서 Kafka Java client와 Kafka 트랜잭션 프로토콜에서도
Consumer.close()무기한 블록, 예측 불가능한 consumer offset, aborted read·lost write·torn transaction 문제가 드러남 - Jepsen은 Kafka 트랜잭션 프로토콜이 클라이언트 요청 순서와 트랜잭션 번호를 명시적으로 보장하지 않아, 공식 Java client 사용 시 Kafka와 Kafka 호환 시스템의 트랜잭션 안전성이 깨질 수 있다고 봄
Bufstream 구조와 검증 범위
- Kafka는 복제·샤딩된 append-only 로그를 제공하는 스트리밍 시스템이며, Bufstream은 클라우드 환경의 데이터 거버넌스와 비용 효율을 우선하는 Kafka 대체 구현임
- Bufstream은 Kafka처럼 topic과 partition을 제공하고, 표준 Kafka client와 동작함
- producer는
producer.send()로 record를 append함 - consumer는
consumer.assign()또는consumer.subscribe()로 partition에 바인딩된 뒤consumer.poll()로 record를 읽음 - consumer group은 topic 집합의 record 처리를 나눠 맡음
- producer는
- Buf Schema Registry와 연동하면 Protocol Buffer record를 검사해 record 검증, field-level access control, 다른 시스템과의 데이터 형식 변환을 지원할 수 있음
- Kafka가 로컬 디스크와 자체 복제 프로토콜을 쓰는 것과 달리, Bufstream은 데이터를 object storage에 직접 기록함
- object storage의 복제 트래픽 비용 구조를 활용해 비용 절감을 노림
- Bufstream node는 stateless auto-scaled VM으로 동작할 수 있음
- 세 하위 시스템이 Bufstream을 구성함
- agent: Kafka API를 제공하는 stateless 서비스
- object store: record chunk를 저장하고 reader에 제공
- coordination service: 현재 etcd를 사용하며, 어떤 chunk가 commit됐는지와 record 순서를 정함
- 2024년 10월 기준 Bufstream은 일부 고객에게만 배포됐고, 문서는 “Apache Kafka의 drop-in replacement”와 Kafka transactions 및 exactly-once semantics 호환성을 내세웠지만 구체적인 안전성 주장은 많지 않았음
클라이언트 설정과 트랜잭션 전제
- Jepsen은 Kafka 호환 시스템의 이전 테스트처럼 더 안전한 동작을 얻기 위해 client 설정을 조정함
-
Producer 설정
- 기본
acks = all을 사용함 - Bufstream에서
acks = 0은 storage 대기 없이 쓰기를 인정할 수 있어 commit된 쓰기를 잃을 수 있음 acks = 1과acks = all은 Bufstream이 durable persist를 확신할 때까지 block함- Kafka producer의 자동 재시도에서 중복 append를 막기 위해 기본값인
enable.idempotence = true를 사용함
- 기본
-
Consumer 설정
- auto-commit이 데이터 손실로 이어질 수 있다는 문서들이 있어 대체로
enable.auto.commit = false를 사용함 - committed offset이 없을 때 기본
auto.offset.reset은 최신 offset에서 시작하므로 at-least-once delivery를 보장하지 않음 - consumer가 전체 로그를 관찰할 수 있도록
auto.offset.reset = earliest를 사용함 - Kafka 트랜잭션은 producer가 보낸 record 집합과 consumer가 poll한 partition별 최대 offset map으로 구성됨
- transaction이 commit될 때에만 보낸 record가 durable하고
read_committedconsumer에 결국 보이며, committed offset도 transaction에 지정된 offset 이상으로 올라감 - transaction이 commit되지 않으면 committed offset은 전진하지 않고, 쓰기 가시성은 consumer 설정에 따라 달라질 수 있음
read_uncommittedconsumer가 abort된 transaction의 값을 읽는 현상은 aborted read(G1a) 로 분류됨- Kafka 문서는
read_committed가 G1a를 막고 transaction의 모든 쓰기가 보이거나 아무것도 보이지 않는 성질을 어느 정도 보장한다고 하지만, Jepsen의 Kafka·Redpanda·Bufstream 테스트에서는 write cycle(G0 유사 현상)과 일부 G1c 형태가 관찰됨
- auto-commit이 데이터 손실로 이어질 수 있다는 문서들이 있어 대체로
테스트 설계
- Jepsen은 Bufstream 0.1.0부터 0.1.3까지와 여러 release candidate build를 테스트함
- 테스트 하네스는 Bufstream test harness, Jepsen testing library, Java Kafka Client 3.8.0을 사용함
-
실행 환경
- Debian Bookworm node 3~5개를 LXC container와 EC2 VM 양쪽에서 사용함
- etcd용 node 1개, Minio용 node 1개, 나머지를 Bufstream agent로 사용함
- producer, consumer, admin client는
bootstrap_servers에 단일 node만 넣어 초기화했지만, smart client discovery는 막지 않았음
-
주요 안전 설정
- auto-commit false
acks = all- retries 1,000
- idempotence enabled
- isolation level
read_committed auto_offset_reset = earliest- server-side automatic topic creation disabled
- 장애 주입은 process pause(
SIGSTOP), crash(SIGKILL), clock skew(clock_settime), network partition(iptables)을 포함함 - Bufstream이 agent, object store, coordination service로 나뉘므로 특정 하위 시스템만 대상으로 장애를 주입할 수 있는 Jepsen 도구를 새로 만듦
- 예를 들어 Bufstream node만 crash하거나 etcd coordinator만 pause하는 식으로 조합을 시간에 따라 바꿈
Queue workload와 Abort workload
- Queue workload는 Kafka 데이터 모델에 맞춰 안전성을 분석함
- 각 logical process는 producer, consumer, admin client를 실행함
- numeric key는 특정 topic-partition을 식별함
- key는 exponential frequency로 선택되어 일부 key는 자주, 일부 key는 드물게 접근됨
- 세 가지 기본 operation을 사용함
crash: logical process를 종료하고 새 client로 대체함subscribe또는assign: consumer가poll할 topic 또는 partition 집합을 바꿈txn,poll,send:poll또는sendmicro-operation의 sequence를 수행함
- non-transactional workload에서는 각
send또는poll이 정확히 하나의 micro-operation만 포함함 - transactional workload에서는 여러 micro-operation을 Kafka transaction으로 감쌈
- 분석은 key별 offset-to-value mapping을 만든 뒤 오류를 찾음
- 같은 offset에서 여러 value가 보이면 inconsistent offset
- 같은 value가 여러 offset에서 보이면 duplicate error
- 인정된 record가 전혀 관찰되지 않으면 lost 또는 unseen
- abort된 operation이 보낸 value를 poll이 반환하면 aborted read
- transaction이 자기 자신의 쓰기를 관찰하는지도 검사함
- main test 뒤에는 장애를 해소하고 final reads 단계로 들어감
- 각 process가 모든 topic-partition을 offset 0부터 읽어, 알려진 최고 written offset까지 poll함
- final reads가 timeout되어 인정된 record가 여전히 관찰되지 않으면 unseen으로 분류함
- Abort workload는 transaction abort 뒤 poll offset 동작을 추적하기 위해 추가됨
- topic은 단일 partition, process, producer, consumer로 제한함
- transaction이 record를 poll한 뒤 의도적으로 abort하고, 이후 poll offset을 advance, rewind, rewind-further, other로 분류함
Bufstream에서 발견된 5개 문제
-
Stuck consumers (#1)
- 0.1.0부터 0.1.3-rc.8까지 final read 단계가 자주 멈춤
consumer.poll()은 즉시 빈 결과를 반환했지만, 로그에는 인정된 record 수천 개가 남아 있었음- 이 상태는 수십 초에서 1시간 이상 지속됨
- 한 테스트에서는 처음 120초 동안 인정된 record 691개를 보냈고, final reads 시작 시점에 40개가 어떤 poller에게도 관찰되지 않음
- 이후 1시간 넘게
consumer.poll()이 결과를 반환하지 않아 테스트가 timeout됨 - 원인은 재시작한 Bufstream node가 last stable offset과 high watermark의 stale cached value를 반환할 수 있었던 점임
- 일부 client library는 더 뒤 record가 없다고 판단해 stall했으며, Bufstream은 startup 시 cache를 refresh하도록 0.1.3-rc.6에서 patch를 적용함
-
Stuck producers & consumers (#2)
- 0.1.3-rc.6에서도 coordinator, storage, Bufstream node에 대한 pause, crash, partition 이후 unseen write 문제가 계속 관찰됨
- 일부 경우 coordinator pause 뒤 모든 Bufstream node가 실행 중인데도 client가
InitProducerId를 기다리다 timeout되는 상태에 들어감 - 다른 경우
listOffsets는node ... being disconnected또는timed out waiting for a node assignment로 실패했고,poll은 완료되지만 결과를 반환하지 않음 - Bufstream node를 kill 후 restart하면 문제가 해소됨
- 원인은 etcd lease와 관련됨
- Bufstream agent는 active agent 추적에 etcd leases를 사용함
- 짧은 pause나 partition 때문에 etcd가 agent lease에 묶인 key를 삭제했지만, 삭제 update가 agent에 전달되지 않을 수 있었음
- agent는 자기 lease를 잃었다는 사실을 모르는 상태가 됨
- Bufstream 팀은 추가 polling logic을 넣었고, 0.1.3-rc.8에서 unseen write는 대체로 해결됨
-
Spurious zero offsets (#3)
- 0.1.0부터 0.1.3-rc.2까지 sent value가 offset
0을 배정받은 뒤 더 높은 실제 offset에서 나타날 수 있었음 - offset
0이 이미 훨씬 전에 배정된 경우에도 발생함 - sender만 offset 0을 관찰했고, poller는 더 높은 offset을 관찰함
- 단일 Bufstream node와 etcd process pause를 넣은 2분 테스트에서는 6개 write가 offset
0을 받았다가 더 높은 offset에 나타남 - 원인은 Bufstream의 error response에 필요한 field가 빠져 있었던 점임
- Bufstream이 etcd에 log commit 요청을 보내고 etcd가 처리했지만, pause나 partition 때문에 Bufstream이 response 대기 중 timeout할 수 있음
- Bufstream은 client에 error code를 보냈지만, sent record offset을 error signal인
-1로 설정하지 않음 - Java Kafka client는 이를 offset
0의 성공 응답으로 해석함 - Bufstream test suite가 사용한 Franz-go는 이 message를 error로 해석해 해당 문제가 테스트에서 드러나지 않았음
- Bufstream은 0.1.3-rc.6에서 수정했고, 이후 Jepsen은 재관찰하지 못함
- 0.1.0부터 0.1.3-rc.2까지 sent value가 offset
-
Lost transaction writes (#4)
- 0.1.2에서는 commit된 transaction의 일부 record가 사라져 다시 관찰되지 않는 write loss가 자주 발생함
- 한 테스트에서는 100초와 write transaction 6,761개 동안 commit된 transaction이 쓴 record 240개가 손실됨
- 예시에서 key
5의 value141은 offset274에 성공적으로 쓰인 것으로 반환됐지만, 모든consumer.poll()은 해당 offset을 건너뜀 - 원인은 0.1.2에 추가된 concurrency safety mechanism의 bug였음
- 이 mechanism은 Kafka transaction protocol의 idempotence 부족을 완화하기 위해 producer epoch 내 transaction마다 unique number를 부여함
- transaction number tracking logic의 bug 때문에 여러 epoch에 걸쳐 여러 transaction이 commit될 때 일부 commit이 잘못 무시됨
- commit된 것처럼 보인 transaction이 실제로 abort되거나 그 반대가 될 수 있었음
- Jepsen은 transaction timeout을 1초로 낮게 둔 덕분에 이 bug를 발견함
- Bufstream은 0.1.2 release 후 몇 시간 안에 문제를 파악했고, 고객 upgrade를 막았으며, 고객은 0.1.2로 upgrade하지 않았음
- 수정은 0.1.3-rc2에 포함됨
-
Server-side filtering으로 인한 lost writes (#5)
- 0.1.3-rc.8에서는 Bufstream process나 coordinator pause, 양자 사이 partition 같은 작은 장애 뒤 짧은 write loss window가 자주 나타남
- data loss는 transaction 사용 여부와 관계없이 발생함
- 한 5분 테스트에서는 16,770개 record 중 22개가 acknowledge됐지만 어떤 consumer도 poll하지 못함
- 일부 record는 한동안 poller에게 보이다가 나중에는 poll에서 사라지기도 함
- 원인은 인기 있는 Kafka web GUI의 bug를 우회하기 위해 0.1.3-rc.8에 추가한 fetch API response size 제한 logic임
- filtering logic의 bug가 lagging consumer에게 record를 숨겼고, write loss처럼 나타남
- Bufstream은 0.1.3-rc.12에서 수정함
Kafka Java client와 Kafka 프로토콜 문제
-
KIP-588: 오해를 부르는 ProducerFencedException
- 테스트 중
ProducerFencedException: There is a newer producer with the same transactionalId which fences the current one.오류가 자주 발생함 - 모든 producer가 unique transactional ID를 받는 테스트에서도 이 오류가 나타나 원인 파악에 시간이 걸림
- KIP-588은 transaction timeout에도
ProducerFencedException이 던져질 수 있다고 적고 있음 - Kafka Java client는 대부분의 timeout에 전용
TimeoutException을 쓰지만, 이 경우에는ProducerFencedException을 던짐 - 실제로 충돌 producer가 없는데도 오류 메시지는 두 번째 producer instance가 존재한다고 말함
- KIP-588은 2년째 열려 있으며, Jepsen은 Kafka 팀이 error message를 바꾸길 권고함
- 테스트 중
-
KAFKA-17734: Consumer.close()가 무기한 block 가능
- Bufstream과 Kafka 양쪽 테스트에서 Java client bug 때문에 몇 시간마다 테스트가 멈춤
Consumer.close()는 기본적으로 network IO에서 block함close()의 timeout parameter는 무기한 block을 막아야 하지만 동작하지 않았음consumer.wakeup()을 별도 thread에서 호출해 IO에 stuck된 consumer를 interrupt하는 방식도 효과가 없었음- Jepsen은 장기 실행 프로그램이 network error에도 client, connection, thread, memory 같은 resource를 합리적 시간 안에 해제할 수 있어야 한다고 보고 KAFKA-17734를 등록함
-
KAFKA-17582: transaction 실패 뒤 consumer offset이 예측 불가능
- Kafka 공식 문서는 transaction commit 실패 시 consumer offset이 어떻게 되어야 하는지 거의 말하지 않음
- Confluent의 Kafka design documentation은 transaction이 abort되면 consumer position이 이전 값으로 돌아간다고 말하지만, 실제 Java client는 항상 그렇게 동작하지 않음
- Abort workload 결과, healthy cluster에서도 abort 뒤 동작은 예측하기 어려웠음
- 대부분의 transaction pair는 더 뒤 offset으로 advance함
- 일부는 이전 offset으로 rewind됨
- 모든 rewind는 rebalance event와 관련됐고, 모든 advance에는 rebalance가 없었음
- Kafka 쪽 답변에 따르면 이 동작은 intentional임
- consumer는 계속 advance함
- rebalance가 발생하면 committed offset에 따라 임의 지점으로 rewind될 수 있음
- 사용자는 transaction abort 시 consumer position을 수동으로 rewind해야 함
- Jepsen은 KAFKA-17582를 열고, 이 동작 문서화와 transaction abort 시 기본 rewind 변경 검토를 제안함
- Queue workload도 consumer를 명시적으로 rewind하도록 수정됨
-
KAFKA-17754: write loss, aborted read, torn transaction
- Bufstream 0.1.0~0.1.3에서 Bufstream process pause, coordinator pause, crash, network partition만으로 aborted read, lost write, atomicity violation이 관찰됨
- 분석은 Kafka transaction protocol의 근본적 결함으로 이어짐
- 예시에서 client는 unique transactional ID
jt1234로 transaction을 실행하고EndTxn에committed = false를 보내 abort했지만, 15개의poll()호출이 abort된 transaction의 write를 관찰함 - 같은 transaction의 다른 write는 어떤 poller도 관찰하지 못함
- packet capture와 Bufstream log를 함께 보면 원인은 지연된 commit message였음
- 몇 transaction 전에 보낸 commit
EndTxn이 한 node에서 늦게 처리됨 - client는 이미 다음 transaction들을 진행하고 있었음
- 지연된 commit이 현재 transaction에 적용되어 transaction의 앞부분만 commit되고, 나머지는 별도 transaction처럼 처리되어 abort됨
- Kafka protocol은 client가 여러 TCP connection과 여러 node에 request를 보낼 수 있도록 설계됐지만, 같은 client의 request 순서를 정하는 sequence number가 없음
- transaction number 개념도 없어 server가 commit 또는 abort message를 받았을 때 client가 어느 transaction을 끝내려 했는지 알 수 없음
- 그 결과 다음 상황이 가능해짐
- commit된 것처럼 보인 transaction이 실제로 abort됨
- abort된 transaction이 실제로 commit됨
- transaction 일부 write만 보존되고 일부는 손실되는 torn transaction이 발생함
- 공식 Java Kafka client는 timeout을 retryable로 취급하고 여러
EndTxnmessage를 자동으로 보낼 수 있어, 사용자가 commit 또는 abort를 transaction마다 한 번만 호출해도 문제가 생길 수 있음 - Jepsen은 Kafka에서도 process pause로 aborted read와 torn transaction을 관찰했고 KAFKA-17754를 열었음
- Kafka engineer들은 KIP-890이 이 문제를 고칠 가능성이 있다고 봄
- KIP-890은 transaction마다 producer epoch를 올리는 방식으로 transaction protocol을 바꿈
- server가 이전 epoch message를 거부하므로 과거 transaction의 commit message가 이후 transaction에 새어 들어가는 일을 막을 수 있음
- Bufstream은 0.1.3에서 etcd revision을 logical clock으로 써 빈도를 줄이는 mechanism을 추가했지만, client와 Bufstream 사이 reorder까지 막지는 못함
- Jepsen은 0.1.3에서도 aborted read, lost write, torn transaction을 계속 관찰했으며, client 쪽 해결이 필요하다고 봄
전체 결과 요약
- Bufstream 자체 문제 5건은 모두 수정됨
- #1: lagging highest stable offset 때문에 consumer가 stuck됨, 장애 불필요, 0.1.3-rc.6에서 수정
- #2: etcd lease expiry 때문에 producer/consumer가 stuck됨, pause 필요, 0.1.3-rc.8에서 수정
- #3: spurious zero offsets, pause 필요, 0.1.3-rc.6에서 수정
- #4: lost transaction writes, 장애 불필요, 0.1.3-rc.2에서 수정
- #5: server-side filtering 때문에 lost writes, pause 필요, 0.1.3-rc.12에서 수정
- Kafka 관련 문제는 여전히 남아 있음
- KIP-588: transaction timeout에서 잘못된 error message, 미해결
- KAFKA-17734:
ConsumerClient.close()가 무기한 block 가능, 미해결 - KAFKA-17582: transaction 실패 뒤 consumer offset이 예측 불가능, 미해결
- KAFKA-17754: write loss, aborted read, torn transaction, 미해결
- Jepsen은 실험적 안전성 검증이 bug의 존재는 증명할 수 있지만 부재는 증명할 수 없다고 주의함
- 특히 KAFKA-17754 때문에 Bufstream에 다른 write loss 사례가 있는지 판별하기 어렵다고 봄
Bufstream 사용자와 운영 권고
- 공식 Java Kafka client로 Bufstream transaction을 사용하는 사용자는 현재 transaction이 안전하지 않을 수 있음을 고려해야 함
- abort된 transaction이 실제로 commit될 수 있음
- commit된 transaction이 실제로 abort될 수 있음
- transaction이 반으로 찢겨 일부 효과만 보존될 수 있음
- Bufstream은 Franz-go client가 이 문제에 덜 취약하다고 보지만, Jepsen은 본 작업과 같은 기법으로 Franz-go를 테스트하지 않았음
- 다른 client는 취약할 수도 있고 아닐 수도 있음
- Bufstream 0.1.3 이전 사용자는 다음 문제를 겪을 수 있음
producer.send()가 실제 offset 대신0offset을 잘못 반환- client가 stuck되는 metastable availability issue
- Jepsen은 0.1.3 upgrade를 권고함
- Bufstream의 전체 아키텍처는 sound해 보인다고 평가함
- etcd 같은 coordination service로 immutable data chunk 순서를 정하는 방식은 OLTP와 streaming system에서 선례가 있는 비교적 단순한 접근임
- 운영 측면에서는 두 가지 개선이 권고됨
- startup 시 storage의 shared file 요청이 실패하면 cluster가 crash할 수 있어 retry를 추가하라고 권고했고, Bufstream은 retry layer를 추가함
- dependency가 unavailable할 때 agent가 즉시 죽기보다 계속 실행되며 backpressure와 system status를 제공하고 더 부드럽게 recover하는 방향을 권고함
- 0.1.3 기준 Bufstream은 etcd에 대한 추가 retry logic을 넣었지만, online 상태 유지를 위해 여전히 constant supervision이 필요함
- 사용자는 process supervisor가 있으며 장기 outage 중에도 포기하지 않고 동작하는지 테스트해야 함
Kafka transaction 문서화와 프로토콜 수정 필요
- Kafka 공식 문서는 transaction에 대해 거의 말하지 않아 사용자는 모호하고 상충되는 여러 source를 조합해야 함
- Jepsen은 Kafka 팀에 transaction semantics를 명확히 정리한 중앙 문서를 만들 것을 권고했고 KAFKA-17671를 언급함
- 해당 문서는 최소한 다음을 명시해야 함
- consumer가 언제 monotonically increasing offset을 관찰하는지
- consumer가 언제 acknowledge된 record를 건너뛸 수 있는지
- rebalance가 transaction 중간에 영향을 줄 수 있는지
- producer write offset이 언제 단조 증가하는지
- G0, G1a, G1b, G1c, fractured read, 자기 transaction write read가 언제 합법인지
- abort된 transaction 뒤
poll()반환값과 offset이 어떤 의미를 갖는지 - transaction error, abort 중 error, rewind 중 error를 어떻게 처리해야 하는지
- Confluent 문서가 Kafka 기본값이 at-least-once delivery를 제공한다고 반복해서 말하지만, Jepsen은 이것이 사실이 아닌 것으로 보인다고 지적함
auto.offset.reset = latest는 처리되지 않은 record를 “committed”처럼 만들 수 있음- Confluent offset management 문서도 기본 auto-commit에서 crash 시 message progress 손실 위험을 말함
- transaction abort 시 consumer가 rewind된다는 문서도 실제와 다름
- Jepsen은 Kafka transaction protocol이 근본적으로 수정되어야 한다고 봄
- protocol은 ordered reliable delivery를 암묵적으로 가정하지만, process pause, network unreliability, non-zero latency, 여러 TCP socket 간 unordered delivery가 존재함
- Kafka protocol은 여러 node와 TCP socket에 message를 분산시키고 client는 message를 자동 retry함
- 같은 client message 순서를 복원할 sequence number와 transaction 대상 확인용 transaction number가 없음
- KIP-890은 transaction commit마다 epoch를 올려 더 엄격한 순서를 보장하려고 함
- client library도 message가 acknowledge되지 않을 때 producer를 re-initialize해 epoch를 올리는 방식으로 도움을 줄 수 있음
- Java Kafka Client 3.8.0은 이 문제에 취약함
- Jepsen은 Franz-go가 timeout 시 re-initialize를 수행해 문제를 완화하거나 방지할 수 있다고 보지만, 다른 client library는 조사하지 않았음
향후 작업
- 많은 사용자는 transaction을 직접 다루기보다 Kafka Streams API의 “exactly-once semantics”에 의존하므로, 향후 Streams application의 정확성을 조사할 수 있음
- Jepsen은 KAFKA-17754를 조사하는 동안 Kafka에서도 unseen write를 만났지만 시간 제약으로 분석하지 못함
- unseen write는 hanging transaction, stuck consumer, data loss의 신호일 수 있음
- 지연된
Producemessage가 미래 transaction에 들어가 transaction guarantee를 위반할 수 있는지도 의문으로 남음 - Kafka Java Client가 request timeout 시 sequence number를 재사용해 write가 acknowledge됐지만 조용히 discard될 가능성도 의심함
- rebalance event가 발생하면 consumer position이 앞뒤로 움직일 수 있으나, 그 규칙은 불명확함
- Kafka가 의도한 동작을 문서화하면 Jepsen은 이를 검증하고 싶어함
- Jepsen은 random process라 드문 anomaly를 탐색하기 어렵다고 설명함
- 한 번 발생하는 문제는 debugging과 reproduction이 매우 어려움
- Bufstream은 deterministic hypervisor와 simulated network에서 분산 시스템 전체를 실행하는 Antithesis도 사용함
- Jepsen의 workload generation 및 history checking을 Antithesis의 deterministic, replayable environment와 결합하면 테스트 재현성을 높일 수 있음