Raw Block — Recovery Bringup Header Validation 병렬화 (NY + DG)
6/5 협의(한대규↔김나연)에서 갈라진 "bringup 시 slot header 직렬 pread 병목" 작업의 현재 코드 상태 정리. 두 브랜치가 같은 함수(
_validate_loaded_entries)를 I/O 엔진별로 분담해 상보적으로 커버한다. 총괄 GitHub 이슈 초안의 근거 노트.
0. 문제 (공통 배경)
RawBlockCore 가 재시작(restart) 시 최신 checkpoint 를 로드해 in-memory index 를
복원한 뒤, 인덱스의 각 엔트리마다 slot header 를 읽어 identity/payload_len 이
metadata 와 일치하는지 검증한다 (_validate_loaded_entries). 기존 구현은:
for encoded_key, entry in items:
slot_hdr = self._read_slot_header(int(entry.offset)) # 슬롯당 동기 pread 1회
...
→ 엔트리 N개면 header 크기(4KiB)짜리 작은 동기 read N회를 직렬로 호출. 엔트리가 많아질수록(대용량 디바이스, 수십만 슬롯) bringup latency 가 선형 증가. 장치 병렬성 (NVMe queue depth)을 전혀 못 쓴다. → 6/5 한대규님이 "entry 많아졌을 때 병목" 으로 지목.
1. 두 브랜치 = 같은 함수의 엔진별 분담
02874b7(DG) 가 _validate_loaded_entries 를 엔진별 dispatch 로 리팩터링하면서
이 함수가 두 작업의 합류 지점이 됐다:
if self.io_engine == "posix" and self._recovery_read_threads > 1 and items:
to_drop = self._validate_loaded_entries_posix_parallel(items) # DG: threadpool
elif self.io_engine == "io_uring":
to_drop = self._validate_loaded_entries_iouring_serial(items) # DG: TODO → NY 가 채움
else:
to_drop = self._validate_loaded_entries_serial(items) # fallback
| 영역 | 담당 | 브랜치 | 메커니즘 |
|---|---|---|---|
| 함수 리팩터링 + dispatch + serial/fallback | DG | dg-raw-block-multithreads-recovery | _validate_loaded_entry() 단위 추출, engine 별 분기 |
| POSIX 경로 | DG | 〃 | ThreadPoolExecutor(8 reader), 엔트리를 range 로 쪼개 병렬 pread |
| io_uring 경로 | NY | perf/validate-batched-read | batched_read 단일 제출 + wait_iouring 로 N개 header 동시 read |
| 공유 bringup 벤치마크 | DG | dg-…(fdaec7f) | raw_block_recovery_bringup_bench.py |
2. DG 커밋 분석
2.1 02874b7 — parallelize POSIX recovery header validation
core.py +120/-21, test +109, design/rst/README 문서 갱신.
핵심 변경:
- 상수
DEFAULT_RECOVERY_READ_THREADS = 8추가. 인스턴스에self._recovery_read_threads보관. (코멘트: 보수적 reader 수로 device 병렬성 노출, 높은 스레드 수에 의존 X. "io_uring recovery should use batched reads" 라고 명시 — NY 작업을 의식한 설계.) _validate_loaded_entries를 monolithic loop → engine dispatch 로 재작성.- 헬퍼 신설:
_validate_loaded_entry(item) -> str | None— 단일 엔트리 검증(stale 면 key 반환). 기존 inline 로직을 그대로 함수화 (identity/payload_len 비교, 예외 시 drop)._validate_loaded_entries_serial—map기반 직렬._validate_loaded_entries_posix_parallel—max_workers=min(8, N),_build_recovery_item_ranges로 엔트리를 연속 range 로 분할 후ThreadPoolExecutor.map으로 range 별 병렬 처리._validate_loaded_entries_iouring_serial— 지금은 serial 로 위임 + TODO 코멘트 ("Add io_uring batch recovery here. Reuse _build_recovery_item_ranges()…"). → 정확히 NY 의perf/validate-batched-read가 구현하는 부분._build_recovery_item_ranges(item_count, num_ranges)—(start, end)리스트._validate_loaded_entry_range_posix(work_item)— range 내 순회하며 stale key 수집.
SLF/타입: 모두 self 내부 메서드, _-prefix 자기 멤버 접근만. 반환 타입 명시
(str | None, list[str], list[tuple[int,int]]). Any/bare generic 없음. ✅
관찰점 (이슈/PR 리뷰용):
_validate_loaded_entry내부except Exception:→ drop. 기존 동작 보존이라 OK.- threadpool 은 read 만 병렬; index/
to_drop적용은 호출부에서 직렬(lock). 안전. - POSIX 만 병렬; io_uring 분기는 일부러 serial 유지(중복/충돌 회피 설계).
2.2 fdaec7f — recovery bringup benchmark
benchmarks/storage_backend_io/raw_block_recovery_bringup_bench.py (+318), README (+26).
- 합성 fixture: 많은 indexed raw-block 엔트리로 checkpoint 를 만들고 slot header 만 기록(payload 본문은 안 씀). bringup = checkpoint load + header validation 측정용.
--prepare(디바이스 덮어쓰기,--i-understand-this-overwrites-device가드) /--measure분리.--threads 1 8스윕,--repeats, median/mean + 1↔8 speedup 출력.time_bringup이raw_block_core.DEFAULT_RECOVERY_READ_THREADS를 직접 바꿔 스레드 수별RawBlockCore생성 시간 측정.- 현재 한계:
make_config가io_engine="posix"하드코딩 → POSIX 경로만 측정. io_uring(batched_read) 경로 측정 기능은 아직 없음 → NY 작업 합류 시 확장 여지.
3. NY 커밋 분석 — dc3f5d36 (perf/validate-batched-read)
core.py +55, test +148. dev 원본(monolithic loop) 위에서 작성된 상태라 DG 의 리팩터링과 같은 함수를 건드림 → 머지 시 충돌, 화해 필요(아래 §4).
- 신설
_read_slot_headers_batched(offsets) -> list[Optional[tuple[int,int]]]:- O_DIRECT 정렬 만족하는 단일 연속 버퍼(
n*hdr + align-1) 할당, 포인터 정렬 pad 계산. batched_read(offsets, views, [hdr]*n)단일 제출 +wait_iouring(batch_id).- 예외 시 전체
[None]*n반환(보수적)._inflight_io_count증감 +_last_io_ts갱신. - 각 view 를
_decode_slot_header로 디코드.
- O_DIRECT 정렬 만족하는 단일 연속 버퍼(
_validate_loaded_entries진입부에서io_engine=="io_uring" and not use_uring_cmd and len(items)>1일 때 batched 경로, 아니면 기존 per-slot read. 이후zip(items, hdrs)로 동일 검증.- uring_cmd(/dev/ngXnY, passthrough) 경로는 제외 —
_read_uring_cmd_buffers가 batched_read 를 안 쓰는 별개 경로이기 때문(io_uring_post_pr3274 §참조).
4. 두 브랜치의 관계 / 머지 순서
- 상보적: POSIX(스레드풀) + io_uring(batched_read) = bringup 검증 병렬화의 양쪽 엔진.
- 충돌 지점: 둘 다
_validate_loaded_entries를 수정. DG 의 dispatch 구조가 "프레임워크", NY 의 batched_read 가 DG 가 남긴_validate_loaded_entries_iouring_serialTODO 를 채우는 구현으로 들어가는 게 자연스러운 화해 방향. - 권장 순서: DG dispatch 리팩터(02874b7) 먼저 머지 → NY 가 그 위에서
_validate_loaded_entries_iouring_serial를_read_slot_headers_batched로 교체. (NY 브랜치를 dispatch 구조에 rebase). 벤치(fdaec7f)는 io_uring threads 측정 확장 가능. - 6/5 협의의 3번(lazy/background verification)은 안 하기로 결정됨(scope 제외) — 이슈/노트에서 제외.
5. 중복 체크 (2026-06-18)
gh issue list --search "recovery header validation bringup"→ 해당 이슈 없음.- 열린 PR 중 #3226/#3449 는 checkpoint 포맷/압축(D1/D2)으로 영역 다름. recovery header validation 병렬화 는 미등록 gap. → 총괄 이슈 신규 등록 타당.