S2 — raw_block checkpoint payload overflow 버그
[!tldr] 업무 관점 takeaway
_index엔트리 수가 checkpoint payload cap(~64MB)을 넘으면 silent fail → 재시작 시 인덱스 전체 유실. 2026-06-04 재평가: Daejun의 #3449(zlib 압축,4.6× 압축률)로 **Std SSD(1.927.68TB)는 사실상 해소**됨. HC SSD(15/30TB)는 여전히 ceiling에 닿음. 수정 전략: "단계 1 PR 불요, HC SSD 전용 단계 2(512MB→2GB) 저우선"으로 축소.
결론 (검증일: 2026-05-27)
실재하는 버그, 재현 완료. _index 엔트리 수가 payload_cap을 초과하면:
- checkpoint가 silent fail (WARNING 한 줄, 예외 없음)
- 재시작 시 인덱스 = 0개 → 전체 캐시 유실
- 데이터는 물리적으로 존재하나 key↔slot 매핑 소실 → 복구 불가
발생 조건 — 엔트리 수 기반, 디바이스 크기 직접 아님
# core.py L1165 경고
"RawBlockCore metadata payload too large (%d > %d), skipping checkpoint"
payload_cap = ((meta_total_bytes // 2) // block_align) * block_align - block_align
= 기본값(128MB, 4096B) → ~64MB
entry당 JSON 크기:
- dense (기본, cached_positions=None): ~209B
- sparse (cached_positions 256개): ~1.2KB
실제 slot_bytes와 overflow 임계점
slot_bytes는 full_chunk_bytes에서 파생 (rust_raw_block_backend.py:259):
slot_bytes = round_up(header_bytes + full_chunk_bytes, block_align)
| 모델 | slot_bytes |
|---|---|
| Llama-3-8B (TP=1) | 32 MB |
| Llama-3-70B (TP=8) | 10 MB |
| Qwen2-72B (TP=4) | 20 MB |
| 설정 | slot_bytes | overflow 시점 (디바이스 가득 찰 때) |
|---|---|---|
| non-layerwise (기본) | 10 MB | 3.1 TB |
| 20 MB | 6.1 TB | |
| 32 MB | 9.8 TB | |
| layerwise=True | ~1 MB | 314 GB |
| sparse (positions) | 10 MB | 0.5 TB |
→ 일반 1TB NVMe + 기본 설정: 안전 (3TB까지 여유) → Std SSD 3.84TB / 7.68TB + 기본 설정: overflow ← 메인스트림 사용자 영향 → HC SSD (15~30TB) + 기본 설정: 확실히 overflow ← Samsung 타겟 → layerwise / sparse: 작은 디바이스도 위험
Qwen3-480B TP=8 실제 영향 디바이스 (TCO 분석 모델)
| 디바이스 | TCO 분류 | 엔트리 수 (가득) | JSON 크기 | overflow |
|---|---|---|---|---|
| 1.92TB | Std SSD | 260K | 52MB | 간당 (no) |
| 3.84TB | Std SSD | 520K | 104MB | YES |
| 7.68TB | Std SSD | 1.04M | 207MB | YES |
| 15TB | HC SSD | 2.08M | 414MB | YES |
| 30TB | HC SSD | 4.16M | 828MB | YES |
→ TCO 분석 모든 Std/HC 옵션이 가득 차면 overflow. HC 전용이 아님.
재현 결과
메커니즘 재현 (repro_s2_checkpoint_overflow.py) ✅ 완료
저장 성공: 60/60 entries (데이터 정상 기록)
checkpoint payload: 10808B > cap 4096B → overflow
_checkpoint_once(force=True) = False ← silent skip
--- 재시작 ---
복구된 _index = 0 ← 전량 유실
선형 스케일링 재현 (repro_s2_midscale.py) ✅ 완료
케이스 1: cap=60KB, 200 엔트리 (0.57×) → OK
케이스 2: cap=60KB, 400 엔트리 (1.13×) → OVERFLOW, 전량 유실
케이스 3: cap=508KB, 2000 엔트리 (0.67×) → OK
케이스 4: cap=508KB, 3500 엔트리 (1.17×) → OVERFLOW, 전량 유실
→ payload_cap 절대값 무관, 엔트리 수 / cap 비율만이 trigger. real default(cap≈64MB) 임계 ≈ 321,000 엔트리.
[[PR-3226-Incremental-Checkpoint|#3226]]과의 관계 확인 (s2_before_after_3226.md)
| 항목 | Before | After #3226 |
|---|---|---|
| base 직렬화 | JSON 전체 | (동일) ❌ |
| overflow 체크 | if len(payload) > payload_cap | (동일) ❌ |
| overflow 시 | warning + return False | (동일, silent) ❌ |
| base 가용 공간 | ~64MB | delta tail 공유로 약간 감소 |
→ S2는 [[PR-3226-Incremental-Checkpoint|#3226]]과 직교. 머지 후에도 동일 코드 경로로 살아있음.
현재 상태 (2026-06-04 재평가)
- Phase 1 (Evidence pack): ✅ 완료
- #3449 재평가: ✅ 완료 — Std SSD 사실상 해소
- zlib 압축
4.6×, capacity +5.5× → Std SSD 1.927.68TB 모두 ceiling 이하 - HC SSD 15/30TB: 여전히 ceiling 초과 (단계 2a만 남음)
- zlib 압축
- 트랙 축소: 단계 1 PR 불요 → 단계 2a(HC SSD 전용, 저우선) 대기 중
#3449(zlib 압축) 적용 후 디바이스별 상태
| 디바이스 | 압축 전 overflow | 압축 후 overflow | 비고 |
|---|---|---|---|
| Std SSD 1.92TB | NO | NO | 원래 안전 |
| Std SSD 3.84TB | YES | NO | #3449로 해소 |
| Std SSD 7.68TB | YES | NO | #3449로 해소 |
| HC SSD 15TB | YES | YES | 여전히 ceiling |
| HC SSD 30TB | YES | YES | 여전히 ceiling |
수정 전략 — 단계 2a (HC SSD 전용, 잔존 작업)
| 단계 | meta_total_bytes | 커버 | 상태 |
|---|---|---|---|
| #3449로 불필요 | |||
| 2a | 512MB → 2GB | HC SSD 15/30TB | 저우선, Daejun 협의 후 |
단계 2a 변경 범위: rust_raw_block_backend.py:234 기본값 변경 + core.py:_DEFAULT_META_VERSION 1→2 bump. 약 10줄.
회귀 테스트: test_rust_raw_block_backend.py의 _FakeRawBlockDevice로 포팅
관련 페이지
- [[raw_block-종단-분석]] — raw_block 전계층, H1~H8 기여 포인트
- [[단기-Task-목록]] — S2 Task 위치
- [[raw_block-개선-Task]] — S2 수정 전략 전체 (단계 1/2a/2b)
- [[raw_block-성능-우선순위]] — D1 = 본 overflow 사안 (설계 tier)
- [[raw_block-checkpoint-recovery-PR]] — daegyu94의 checkpoint 복구 강화 (같은 경로, 인접 작업)
- [[PR-3226-Incremental-Checkpoint]] — S2와 직교한 PR (base JSON 동일, overflow 미해결)