본문으로 건너뛰기

S2 종합 분석 — raw_block checkpoint payload overflow

검증일: 2026-05-27 / 종합 정리: 2026-05-28 / #3449 재평가: 2026-06-04 대상: lmcache/v1/storage_backend/raw_block/core.py 관련 task: private/tasks/v3_standalone_items.md S2 재현 스크립트: private/work/s2_checkpoint_overflow/repro_checkpoint_overflow.py


0. 한 줄 요약

raw_block은 디바이스의 "목차(인덱스)"를 메타 영역에 JSON으로 백업하는데, 이 JSON에 ~64MB라는 보이지 않는 천장이 있고, 대용량 SSD를 가득 채워 엔트리가 ~32만 개를 넘으면 백업이 **조용히 실패(silent fail)**해서 재시작 시 캐시 전체를 잃는다 — 그리고 이건 정확히 대규모 disk-tier(HC SSD 포함) 사용 시나리오에서 드러난다.

2026-06-04 재평가 — #3449 (zlib 압축) 반영: Daejun의 후속 PR #3449가 checkpoint payload를 zlib 압축(실측 4.6x, capacity +5.5x)해 동일 ceiling 안에 ~5x 더 많은 엔트리를 수용. 이로 인해 Std SSD(1.927.68TB) 케이스는 사실상 해소됨. HC SSD(15/30TB)는 여전히 ceiling에 닿음 — §3 재계산 참고. S2 트랙: "단계 1 PR 불요, HC SSD 전용 단계 2 저우선"으로 축소. §9 갱신.


1. 버그 메커니즘

디바이스 레이아웃 (core.py:975-990)

┌─────────────────────────┬──────────────────────────────────────┐
│ ① 메타데이터 영역 │ ② 데이터 영역 │
│ (offset 0 ~ 128MB) │ (128MB ~ 디바이스 끝) │
│ 인덱스(key→offset 맵) │ 고정 크기 slot_bytes 슬롯 배열 │
└─────────────────────────┴──────────────────────────────────────┘
  • 데이터 영역: 실제 KV 캐시. 키 1개 = 슬롯 1개.
  • 메타 영역: "어떤 키가 몇 번 슬롯에 있는지" 인덱스. 기본 128MB.

런타임 인덱스는 RAM에 (core.py:241)

self._index: dict[str, _Entry] = {} # 키 → _Entry(offset, size, meta)

휘발성. 재시작하면 증발 → 데이터 영역의 KV가 멀쩡해도 슬롯↔키 매핑을 잃음.

Checkpoint = 인덱스를 디바이스에 박제 (core.py:1213, 1102)

snapshot, _ = self._snapshot_state() # _index → dict
payload = json.dumps(snapshot, ...).encode() # → JSON 바이트
return self._write_checkpoint(payload, ...) # → 메타 영역에 기록

엔트리 1개 JSON ≈ 209B (dense):

"model@0@<64자 해시>": {"offset":...,"size":...,"shape":[...],
"dtype":"bfloat16","fmt":"...","cached_positions":null}

천장: payload_cap (core.py:1032, 231)

메타 영역은 2벌 미러링(_meta_copy_count=2)이라 한 벌당 용량:

payload_cap = (meta_total_bytes // 2 정렬) - block_align
기본값(128MB, 4096) → ~64MB

버그: 천장 넘으면 silent skip (core.py:1164-1171)

if len(payload) > payload_cap:
logger.warning("metadata payload too large, skipping checkpoint")
return False # ← 예외 없음, WARNING 한 줄, 그냥 안 씀

복구 경로는 checkpoint가 유일 (확인 완료)

  • _validate_loaded_entries(core.py:1407)는 checkpoint에서 로드된 _index검증만 함. 슬롯을 스캔해 인덱스를 재구축하는 코드 없음.
  • 슬롯 헤더(_encode_header, core.py:940)에는 slot_identity(8바이트 해시) + payload_len만 있고 원본 키 문자열이 없음 → 헤더만으로 키 복원 불가.
  • ∴ checkpoint JSON이 키→offset 매핑의 유일한 영구 저장소. fallback 없음.

실패 시나리오

인덱스 누적 → JSON > 64MB → checkpoint 매번 silent skip
→ 메타 영역은 옛 상태(또는 meta_seq=0 빈 상태)
→ 운영자 모름 (WARNING 한 줄, 알림/예외 없음)
→ 재시작/크래시 → RAM의 _index 증발
→ 메타 영역에서 복구 시도 → 비었거나 옛것 → 데이터 영역 KV 전부 미아
→ 캐시 전량 유실 (물리적으론 디스크에 있으나 못 찾음)

2. 초기 분석 오류 정정

초기 검토(2026-05-26)에서 "1TB 디바이스에서 지금 당장 실패"라 주장 → 틀림. 원인: slot_bytes를 1MB로 오가정.

  • slot_bytes는 full_chunk_bytes에서 파생 (rust_raw_block_backend.py:259, local_cpu_backend.py:792)

  • non-layerwise: full_chunk_bytes = kv_size × num_layers × chunk_tokens × hidden_dim × dtype (local_cpu_backend.py:816)

  • 실측 slot_bytes:

    모델slot_bytes
    Llama-3-8B (TP=1)32 MB
    Llama-3-70B (TP=8)10 MB
    Qwen2-72B (TP=4)20 MB

→ 1MB가 아니라 1032MB. 초기 계산이 1030배 과장.


3. 정확한 overflow 임계점

  • 임계: payload_cap / entry_size = 64MB / 209B ≈ 321,000 entries
  • = 약 82M 토큰의 distinct KV (chunk_size=256 기준)
  • entry 크기: dense(cached_positions=None) ~209B / sparse(256 positions) ~1.2KB
  • use_layerwise 기본 False (config.py:99). layerwise=True면 slot ≈ 1MB
설정slot_bytesoverflow (가득 찰 때)
non-layerwise (기본)10 MB3.1 TB
20 MB6.1 TB
32 MB9.8 TB
layerwise=True~1 MB314 GB
sparse (positions)10 MB0.5 TB

디바이스 크기 × 모델별 (가득 찰 때 엔트리 수)

디바이스모델(slot)max 엔트리JSONoverflow
1.92TB StdLlama-70B(10MB)201K40MBno
3.84TB StdLlama-70B(10MB)403K80MBYES
7.68TB StdLlama-70B(10MB)805K161MBYES
7.68TB StdLlama-8B(32MB)252K50MBno
15TB HCLlama-8B(32MB)503K100MBYES
30TB HCLlama-8B(32MB)983K196MBYES

핵심 발견: HC SSD 전용 문제가 아님. 3.84/7.68TB는 TCO 문서의 "Std SSD"(Option B). 큰 모델(70B, TP로 slot 작아짐)에선 표준 엔터프라이즈 SSD에서도 가득 차면 overflow. 규칙: slot이 작을수록(큰 모델+높은 TP, 또는 layerwise) 같은 용량에 엔트리 ↑ → 더 쉽게 넘음.

#3449 (zlib 압축) 반영 후 재계산 (2026-06-04)

#3449 실측: 동일 payload_cap에서 압축 후 ~5.5x 더 많은 엔트리 수용 (20K entries 기준). 실효 임계 엔트리: 321K → 1.61.75M (보수 추정 ~5x 적용 시 ~1.6M).

Qwen3-480B TP=8 (slot 7.75MB, §6b 기준) 재계산:

디바이스max 엔트리#3449 전 overflow#3449 후 overflow
1.92TB Std260Knono
3.84TB Std520KYESno (압축 후 임계 ~1.6M 밑)
7.68TB Std1.04MYESno
15TB HC2.08MYESYES (임계 초과)
30TB HC4.16MYESYES

Std SSD는 #3449로 사실상 해소. HC SSD(15/30TB)는 여전히 살아있음.

Llama-70B TP=8 (slot 10MB) 기준도 유사 — 3.84TB(403K)는 해소, 15TB+(1.5M+)는 잔존.


4. 재현 (검증 완료)

private/work/s2_checkpoint_overflow/repro_checkpoint_overflow.py_FakeRawBlockDevice(메모리 bytearray)로 실제 디바이스 없이 동일 메커니즘 재현. payload_cap을 작게(4KB) 축소.

payload_cap = 4096 bytes
저장 성공: 60/60 entries, _index 크기 = 60 ← 데이터는 정상 기록
checkpoint payload 크기 = 10808 bytes (cap 4096)
→ overflow? True
_checkpoint_once(force=True) 반환값 = False ← silent skip
meta_seq = 0 ← 한 번도 안 써짐
--- 재시작 (같은 디바이스 재오픈) ---
복구된 _index 크기 = 0 ← 60개 전부 유실

로그 (WARNING만, 예외 없음):

RawBlockCore metadata payload too large (10808 > 4096), skipping checkpoint (core.py:1165)

핵심: 버그는 디바이스 물리 크기가 아니라 엔트리 개수로 터짐 → 8KB 가짜 디바이스로 15TB SSD에서 일어날 일을 동일 재현 가능.


5. 실제 사용성 평가 (실재 이슈인가?)

실재 이슈가 맞다는 근거

  1. 복구 경로가 checkpoint 하나뿐 (fallback 없음, 슬롯 스캔으로도 불가).
  2. 타겟 하드웨어(대용량 SSD)가 설계대로 쓰면 반드시 임계점 도달.
  3. disk 티어의 가치 제안("재시작에도 캐시 생존")을 정면으로 무너뜨림.
  4. silent — 재시작 전까지 아무도 모름.

덜 급할 수 있다는 정직한 반론

  1. raw_block이 신생 기능 (§7 참조) — 현재 HC-SSD 스케일 프로덕션 사용자 사실상 없음.
  2. load_checkpoint_on_init=False로 끄면(영속성 불요 배포) 무관.
  3. meta_total_bytes를 키워두면 회피 가능한 튜닝 노브.

종합

코드 트집이 아닌 진짜 버그지만 성격은 잠복형(latent) 스케일 버그. 기능이 의도한 규모로 성공할 때 비로소 문다. 가장 정확한 프레이밍:

"대용량 SSD를 LMCache disk tier로 쓰세요"라고 고객에게 말하기 전에 고쳐야 할 버그.


6. 실제 AI 워크로드 발생 가능성

인덱스는 현재 디바이스 거주 키만 담음 → 두 조건 필요:

  1. 디바이스가 가득 차면 엔트리 > 32만? (디바이스 크기 + slot_bytes 의존, §3 표)
  2. 워크로드가 디바이스를 그만큼 채우나? (distinct 작업셋 크기 의존)

82M distinct 토큰이 현실적인 시나리오:

워크로드82M 토큰 환산현실성
대규모 RAG~120,000 페이지 코퍼스엔터프라이즈 지식베이스면 가능
다중 사용자 챗~8,200 distinct 활성 대화(~10K토큰)고트래픽 서비스면 도달
에이전트/툴긴 컨텍스트 + 도구 출력 누적컨텍스트 길수록 빨리

핵심 통찰: disk 티어는 채우려고 provision하는 것. LRU는 steady state에서 항상 가득 참 → 인덱스 크기 ≈ 디바이스가 찬 정도. disk 티어를 정당화하는 워크로드(대형 RAG, 고트래픽)가 곧 distinct 작업셋이 큰 워크로드. 자기선택적 — 이 하드웨어 까는 사람이 이 버그 밟을 사람.

안 일어나는 경우(균형): 소규모(1TB 이하 + 8B), 고재사용(distinct 작음), 개발/단일 GPU, 영속성 불요 배포.

발생 조건 한 문장:

(큰 모델 또는 layerwise로 slot 작고) + (3TB+ 디바이스) + (대형 RAG/고트래픽으로 distinct 작업셋이 커서 가득 채우는) 배포 → steady state에서 반드시 도달.


6b. Qwen3-Coder-480B 사례 (HC-SSD 비용 분석과의 연결)

HC-SSD 비용 분석(private/docs/lmcache_disk_tco_analysis_ko.md) 당시 사용한 모델이 Qwen3-Coder-480B. 이 모델이 정확히 S2를 트리거하는 프로파일임.

주의: 아래는 추정 아키텍처(num_layers=62, num_kv_heads=8(GQA), head_dim=128) 기준. 정확히는 실제 config.jsonnum_hidden_layers/num_key_value_heads/head_dim 확인 필요. 단 "크고 GQA 사용"인 한 결론(slot 작아 overflow 취약)은 견고.

slot_bytes가 작음 (overflow에 불리)

480B MoE라도 KV 캐시는 attention만 관여(expert 수 무관). GQA로 KV head 8개 → 8-GPU 노드(TP=8)면 GPU당 1 head:

TPper-GPU KV headslot_bytes
TP=8 (단일 노드, 현실 배포)17.75 MB
TP=4215.5 MB
TP=2431 MB

480B는 최소 8-GPU 필요 → 현실 TP=8 → slot 7.75MB (작음 = 엔트리 많음 = overflow 취약).

overflow 임계 디바이스 크기

TP=8 (slot 7.75MB): 2.4TB 가득 차면 overflow 시작
TP=4 (slot 15.5MB): 4.7TB 가득 차면 overflow 시작

비용 분석 디바이스 옵션 대입 (TP=8, 가득 찰 때):

디바이스TCO 분류max 엔트리JSONoverflow
1.92TB(소형)260K52MBno (간당간당)
3.84TBStd SSD520K104MBYES
7.68TBStd SSD1.04M207MBYES
15TBHC SSD2.08M414MBYES
30TBHC SSD4.16M828MBYES

TCO 분석의 Std SSD(3.84/7.68TB)·HC SSD(15/30TB) 옵션 전부가 이 모델로 가득 차면 overflow. 안전한 건 1.92TB 정도뿐.

"실제로 가득 차나" 조건도 충족

Coder 모델 + 긴 컨텍스트(256K)는 §6의 "distinct 작업셋 큰 워크로드" 프로파일 정확히 해당:

  • 대형 코드베이스/긴 파일/repo 수준 컨텍스트 → distinct 콘텐츠 많음
  • 새 코드/파일 계속 유입, 반복 적음 → 캐시가 빠르게 distinct로 참
  • 82M distinct 토큰(=TP=8 기준 2.4TB)을 며칠~몇 주 운영이면 도달
  • §6의 "안 일어나는 경우(고재사용·소규모)"가 이 모델엔 해당 안 됨

비용 분석에의 함의

TCO 분석의 전제는 "캐시가 누적·유지되어 GPU 비용을 절감"인데, S2가 이 전제를 무너뜨림:

"이 SSD로 Qwen3-480B 캐싱 → GPU 비용 X 절감"이라는 결론이, 재시작 시 캐시 전량 유실 → 절감 효과 리셋 리스크를 깔고 있음. S2 미수정 시 TCO 분석의 ROI가 낙관적으로 과대평가됨.


7. "LMCache는 널리 쓰이는데 왜 안 터졌나"

LMCache(프로젝트)와 raw_block(S2가 있는 백엔드)을 구분해야 함.

LMCache — 성숙·널리 배포

  • 2년차 (2024-05-29 시작, 1,612 커밋)
  • 통합: vLLM, vLLM Production Stack, llm-d, NVIDIA Dynamo, KServe
  • 스토리지 파트너: Redis, Weka, PliOps (공개 블로그)

raw_block — 신생·미배포 (repo 증거)

  1. 최초 등장 2026-02-04 (#2482) — 2년 프로젝트에서 ~4개월.
  2. user-facing 백엔드 카탈로그에 없음. docs/source/kv_cache/storage_backends/에 raw_block 부재 (문서화된 건 3fs/cpu_ram/dax/gds/redis/s3/weka 등). MP L2 어댑터 문서(mp/l2_storage.rst)에만 등장.
  3. MP L2 adapter 통합(#3119) 머지 2026-05-04 (3주 전). 삼성 팀이 활발히 개발 중.

결론

프로덕션 배포들은 raw_block을 안 씀 — 성숙한 다른 백엔드(Redis/Weka/local_storage/S3) 사용. 이들은 파일시스템의 inode/디렉토리가 인덱스를 대신 관리해 S2가 구조적으로 없음. raw_block은 파일시스템을 일부러 우회(NVMe 직접 I/O, O_DIRECT)하는 게 존재 이유라 인덱스를 스스로 관리해야 하고, 그래서 checkpoint가 필요하고, 그래서 S2가 생김.

"아무도 안 밟음 = 버그 없음"이 아니라 "이 기능이 아직 그 규모로 안 깔림". 삼성 입장: "남이 안 밟은 버그"가 아니라 "우리가 곧 밟을 길 위의 버그".


8. MP / non-MP 양쪽 영향

S2는 공유 RawBlockCore에 있어 MP/non-MP 공통. (디자인 문서: "avoids maintaining separate raw-block implementations for MP and non-MP mode")

non-MP(RustRawBlockBackend) 기본값도 MP와 동일:

설정non-MP 기본출처
meta_total_bytes128MB → payload_cap 64MBrust_raw_block_backend.py:234
meta_enable_periodicTrue:279
load_checkpoint_on_initTrue:282

overflow 코드(_snapshot_state/_write_checkpoint)는 namespace 무관_index를 JSON 직렬화할 뿐. MP "object" 키든 non-MP "legacy" 키든 동일.

모드별 차이 (eviction 주체)

eviction 주체인덱스 누적
MP외부 SM이 L2 eviction 관장SM 예산만큼 (디바이스보다 작을 수도)
non-MP자체 eviction 없음 (= S1)max_slots까지 차고 그 뒤 신규 드롭

non-MP는 S1과 맞물려 인덱스가 max_slots까지 자연 누적 → 대용량 디바이스에서 가득 차면 S2 도달. non-MP가 더 오래된 원조 경로(2026-02-04)라 S1·S2가 함께 사는 가장 "날 것" 경로.

"non-MP면 안전"은 아님.


9. 수정 방향 — Std SSD / HC SSD 분리 전략

2026-05-29 갱신: Std SSD(1.927.68TB)와 HC SSD(1530TB+)는 사용자 프로파일과 fix 정당성이 다름. 단계 분리가 효율적.

2026-06-04 재평가 — #3449 반영으로 단계 전략 변경: #3449(zlib 압축)가 Std SSD 케이스를 사실상 해소. 단계 1 PR은 더 이상 불요. HC SSD(15/30TB)만 잔존 → 저우선으로 변경. 단계 2만 후보로 유지.

단계별 커버리지 요약 (#3449 반영 갱신)

단계방식Std SSD 커버HC SSD 커버우선순위
#3449 (압축, Daejun)zlib 압축 ~5x✅ 해소❌ 15/30TB 잔존머지 대기
1 (Std SSD bump)128MB → 512MB불요 (폐기)
2a (HC bump)128MB → 2GB✅ ~30TB저우선
2b (binary RFC)바이너리 포맷저우선 (장기)

단계 1 — 폐기 (2026-06-04)

#3449 zlib 압축으로 Std SSD 케이스 해소 → 단계 1(128MB → 512MB) PR은 더 이상 필요 없음.

단계 2 — HC SSD 커버 (저우선, #3449 머지 후 검토)

두 옵션 중 선택 (또는 병행). #3449가 머지·안정화된 후 Samsung HC SSD 데이터로 재검토.

옵션 2a — meta_total bump (128MB → 2GB, 총 16x)

임계 엔트리: 321K → 5.14M
임계 디바이스 (Qwen3-480B TP=8): 2.4TB → 38TB
  • 15/30TB HC SSD까지 커버 (layerwise 제외)
  • 비용: 30TB에서 0.007% 잠식 — 사실상 무료
  • 정당성: Samsung HC SSD TCO 분석 데이터 + #3449 후 잔존 케이스 증거 필요
  • 머지 난이도: 중간 (압축과 함께 쓰면 더 효과)

옵션 2b — 바이너리 base 포맷 RFC (장기 본질 해결)

entry당 ~209B(JSON) → ~30B(고정 길이 바이너리)
같은 천장으로 약 7배 더 많은 엔트리 + layerwise 모드도 커버
  • 장점: 천장 자체를 키우지 않고도 헤드룸 확보, 직렬화 빠름, layerwise 처리
  • 단점: 포맷 설계 + _snapshot_state/_apply_loaded_state 전면 수정
  • 머지 난이도: 높음 (RFC 토론 3-6개월)

추천: #3449 머지 후 Samsung HC SSD 실측 보강 → 2a 또는 2b 결정.

반드시 — 회귀 테스트 (단계 1과 묶음)

private/work/s2_checkpoint_overflow/repro_checkpoint_overflow.py / repro_s2_midscale.pytests/v1/storage_backend/test_rust_raw_block_backend.py_FakeRawBlockDevice/_make_raw_block_core 헬퍼로 포팅. 수정 후 통과하도록.

추천 PR 경로

단계 1 (지금): meta_total_bytes 128MB → 512MB + meta_version bump + 회귀 테스트 → 1개 PR. "보수적 modest fix, Std SSD 메인스트림 사용자 영향". 머지 가능성 높음.

단계 2 (단계 1 머지 후): 옵션 2a 또는 2b. Samsung HC SSD 데이터 보강 후 결정.

PR 명분의 핵심 근거: 실측 + 재현 + 임계점 표 (이미 확보).


10. 2026-06-04 재평가 — #3449 (zlib 압축) 반영

PR #3449 정독일: 2026-06-04 / 상태: OPEN, DongDongJu APPROVED (2026-06-01), ApostaC doc 코멘트만 남음 → 머지 임박

#3449 변경 요약

  • checkpoint payload를 zlib level 1로 압축 (meta_checkpoint_compression="zlib", default)
  • on-device 포맷: magic tag(b"LMCZ1\x00") 프레임. 구버전(uncompressed) payload는 {(0x7B)로 시작 → 자동 구분, 하위 호환 유지
  • meta_total_bytes / payload_capacity / _write_checkpoint silent skip 로직 자체는 무변경 — ceiling 동일
  • meta_version 미변경 (magic tag로 처리)
  • 실측 (real device, 20K entries): payload 4332KB → 952KB (4.6x 압축), capacity ~100 → ~550 entries 가득 찰 때 (5.5x)
  • 압축/해제는 백그라운드 checkpoint 스레드 / 재시작 시 1회 → KV 데이터 경로 무영향

임계점 재계산 (Qwen3-480B TP=8, entry~209B, 4.6x 압축)

디바이스원본 JSON압축 후 (~÷4.6)cap(64MB)overflow?기존
1.92TB52MB~11MB64MB✅ 안전안전 (불변)
3.84TB104MB~23MB64MB✅ 안전기존 overflow → 해소
7.68TB207MB~45MB64MB✅ 안전 (여유 19MB)기존 overflow → 해소
15TB HC414MB~90MB64MB❌ overflow여전히 overflow
30TB HC828MB~180MB64MB❌ overflow여전히 overflow

Llama-70B (10MB 슬롯, 동일 패턴):

  • 15TB: 300MB → ~65MB → borderline overflow (1MB 초과)
  • 30TB: 600MB → ~130MB → overflow

S2 트랙 재평가 결론

→ (b) 축소: Std SSD(3.84/7.68TB) overflow는 #3449 머지로 해소. HC SSD(15/30TB)만 살아있음.

단계 분리 갱신:

단계기존갱신비고
단계 1 (128MB→512MB)Std SSD 커버#3449로 불요#3449 머지 후 폐기
단계 2a (2GB bump)HC SSD 30TB까지우선 후보Samsung HC SSD 데이터 보강 권장
단계 2b (binary RFC)layerwise까지장기 후보3-6개월
  • Daejun 협의: 질문 대상이 #3226 → #3449로 갱신. 질문 초점도 "overflow 다루나요?" → "HC SSD(15/30TB)에서 여전히 닿는 overhead 어떻게 볼지"로 전환.
  • S2 트랙은 폐기가 아닌 저우선 유지 — HC SSD에서 살아있으나 메인스트림 우선도 낮아짐.
  • S2 단계 1 PR 준비 자료(s2_todo.md P3-2)는 HC SSD용 단계 2a 준비로 전환 또는 보류.