- 분산 메시징 시스템 NATS JetStream의 내구성과 일관성을 Jepsen이 다양한 장애 환경에서 검증
- 테스트 결과, 파일 손상(.blk, snapshot) 및 전원 장애 시뮬레이션에서 데이터 손실과 split-brain 현상이 발생
- JetStream은 기본적으로 fsync를 2분마다 수행해, 최근 승인된 메시지가 디스크에 기록되지 않은 상태로 남을 수 있음
- 단일 노드의 OS 크래시만으로도 데이터 손실과 복제본 불일치가 유발될 수 있음
- Jepsen은 NATS에 fsync=always 기본 설정 변경 또는 데이터 손실 위험 명시적 문서화를 권고
1. 배경
-
NATS는 메시지를 스트림으로 발행·구독하는 인기 있는 스트리밍 시스템
- JetStream은 Raft 합의 알고리듬을 사용해 데이터를 복제하고, 최소 한 번(at-least-once) 전달을 보장
- JetStream은 문서상 Linearizable 일관성과 항상 가용성을 주장하지만, CAP 정리에 따라 두 조건을 동시에 만족할 수 없음
- NATS 문서에 따르면 3노드 스트림은 1대, 5노드 스트림은 2대의 서버 손실을 견딜 수 있음
- 메시지는 서버가 publish 요청을 acknowledge한 시점에 “성공적으로 저장됨”으로 간주
- 데이터 일관성을 위해 과반수(quorum) 노드가 필요하며, 5노드 클러스터에서는 최소 3대가 동작해야 새 메시지를 저장 가능
2. 테스트 설계
- Jepsen은 JNATS 2.24.0 클라이언트와 Debian 12 LXC 컨테이너 환경에서 테스트 수행
- 일부 테스트는 Antithesis 환경에서 공식 NATS Docker 이미지를 사용
- 단일 JetStream 스트림(복제 5)을 구성하고, 프로세스 중단·충돌·네트워크 분할·패킷 손실·파일 손상 등을 주입
-
LazyFS 파일시스템을 사용해 fsync되지 않은 쓰기를 손실시키는 전원 장애 시뮬레이션 수행
- 각 프로세스는 고유 메시지를 발행하고, 테스트 종료 후 모든 노드에서 acknowledged 메시지의 존재 여부를 검증
- 메시지가 일부 노드에서만 존재할 경우 divergence(복제 불일치) 로 분류
3. 주요 결과
3.1 NATS 2.10.22의 전체 데이터 손실 (#6888)
- 단순 프로세스 충돌만으로 JetStream 스트림 전체가 사라지는 현상 발견
-
"No matching streams for subject" 오류 발생 후 수 시간 동안 복구되지 않음
- 원인은 리더 스냅샷 역전, Raft 상태 삭제 등으로, 2.10.23 버전에서 수정됨
3.2 .blk 파일 손상 시 데이터 손실 (#7549)
- JetStream의 .blk 파일에 단일 비트 오류나 잘림(truncation) 발생 시 수십만 건의 승인된 쓰기 손실
- 예: 1,367,069건 중 679,153건 손실
- 일부 노드만 손상되어도 대규모 데이터 손실 및 split-brain 발생
- 예: 노드 n1, n3, n5에서 최대 78% 메시지 손실
- NATS는 해당 문제를 조사 중
3.3 스냅샷 파일 손상 시 전체 데이터 삭제 (#7556)
-
data/jetstream/$SYS/_js_/ 내 스냅샷 파일이 손상되면, 노드가 스트림을 고아(orphaned) 로 판단하고 데이터 전체 삭제
- 소수 노드만 손상되어도 클러스터 과반수 불가 및 스트림 영구 불가용
- 예: 노드 n3, n5 손상 → n3이 리더로 선출되어 jepsen-stream 전체 삭제
- Jepsen은 리더 선출 시 손상된 노드가 리더가 되는 위험성을 지적
3.4 기본 fsync 설정으로 인한 데이터 손실 (#7564)
- JetStream은 기본적으로 2분마다만 fsync 수행, 메시지는 즉시 승인
- 결과적으로 최근 승인된 메시지가 디스크에 기록되지 않은 상태로 남음
-
전원 장애나 커널 크래시 시 수십 초 분량의 승인된 메시지 손실 발생
- 예: 930,005건 중 131,418건 손실
- 단일 노드 장애가 연속 발생해도 전체 스트림 삭제 가능
- 문서에는 이 동작이 거의 언급되지 않음
- Jepsen은 fsync=always 기본값 변경 또는 데이터 손실 위험 명시적 경고를 권장
3.5 단일 OS 크래시로 인한 split-brain (#7567)
- 단일 노드의 전원 장애 또는 커널 크래시만으로도 데이터 손실 및 복제 불일치 발생 가능
- 리더-팔로워 구조에서 일부 노드가 메모리에만 커밋된 상태로 승인 후 장애 발생 시,
다수 노드가 해당 쓰기를 잃고 새로운 상태로 진행
- 테스트에서 단일 전원 장애 후 지속적 split-brain 발생
- 노드별로 서로 다른 구간의 승인 메시지 손실 확인
- Jepsen은 Kafka의 유사 사례를 인용하며, Raft 기반 시스템에서도 동일 위험 존재를 강조
4. 논의 및 결론
- 2.10.22의 전체 데이터 손실 문제는 2.10.23에서 해결
- 2.12.1에서는 파일 손상 및 OS 크래시로 인한 데이터 손실·split-brain이 여전히 발생
-
.blk 및 스냅샷 파일 손상 시 일부 노드에서 메시지 누락 또는 전체 스트림 삭제 발생
- 기본 fsync 주기가 길어 복수 노드 동시 장애 시 승인된 데이터 손실 위험 존재
- Jepsen은 fsync=always 설정 또는 문서 내 명확한 위험 고지를 제안
- JetStream의 “항상 가용” 주장은 CAP 정리상 불가능, 문서 수정 필요
- Jepsen은 버그 존재는 입증 가능하지만, 안전성의 부재는 증명 불가함을 명시
4.1 LazyFS의 역할
-
LazyFS를 이용해 fsync되지 않은 쓰기 손실을 시뮬레이션
- 전원 장애 시 부분적 쓰기 손상(torn write) 등 다양한 스토리지 오류 재현 가능
- 관련 연구 *When Amnesia Strikes (VLDB 2024)*에서 PostgreSQL, Redis, ZooKeeper 등에서도 유사 버그 보고
4.2 향후 과제
- 단일 소비자 수준의 메시지 손실, 메시지 순서, Linearizable/Serializable 보장 검증은 미실시
-
정확히 한 번(exactly-once) 전달 보장도 향후 연구 대상
- 노드 추가·제거 시 문서 오류 및 필수 health check 단계 누락 발견 (#7545)
- 안전한 클러스터 구성 변경 절차는 아직 불명확