delete_many 해석
변경 검증 가이드 (다음 fetch 후):
git log eaa2bfee..HEAD -- lmcache/v1/storage_backend/raw_block/core.py위에 커밋이 잡히면
delete_many의 ① ~ ⑩ 단계 (인덱스 락 / inflight 취소 / free list 회수 / dirty 카운터) 가 그대로 유지되는지 line-by-line 재확인. 특히 NVMe TRIM 이 추가됐다면 "delete 가 안 하는 일" 표 + H6 후보 언급을 갱신해야 함.
위치: lmcache/v1/storage_backend/raw_block/core.py:653-692
한 줄 요약
여러 키를 한 번에 삭제하면서, 슬롯을 free list 로 회수하고, lock 걸린 키는 보호하고, 진행 중(inflight) 인 put 도 취소 처리하는 함수.
캐시 evict 의 실제 회수 동작.
입출력
delete_many(["key_A", "key_B", "key_C"], force=False)
→ [True, False, True]
- 입력: 삭제할 키 리스트 +
force플래그 - 출력: 같은 길이의 삭제 성공 bool 리스트
force=False (기본): "lock 걸린 거 빼고 지워줘"
force=True: "강제로 다 지워줘" (close/cleanup 시점)
코드 한 줄씩
deleted: list[bool] = []
with self._lock: # ① 인덱스 락
for encoded_key in encoded_keys:
existed = encoded_key in self._index or encoded_key in self._inflight # ②
entry = self._index.get(encoded_key)
locked = self._lock_refcnt.get(encoded_key, 0) > 0 # ③
if entry is not None and locked and not force:
deleted.append(False) # ④ 보호
continue
removed_entry = self._index.pop(encoded_key, None) # ⑤ 인덱스 제거
inflight = self._inflight.get(encoded_key)
if inflight is not None:
inflight.canceled = True # ⑥ 진행중 put 취소
self._lock_refcnt.pop(encoded_key, None) # ⑦ refcount 정리
if removed_entry is not None:
self._append_free_slot_locked( # ⑧ 슬롯 회수
self._offset_to_slot(int(removed_entry.offset))
)
self._meta_dirty_total += 1 # ⑨ 체크포인트 dirty
deleted.append(
existed and (removed_entry is not None or inflight is not None) # ⑩
)
return deleted
① with self._lock
_index, _lock_refcnt, _inflight, _free_slots 다 만지므로 보호.
② "존재 여부" 는 두 군데를 본다
_index: 디스크 쓰기 완료된 키_inflight: 아직 디스크에 쓰는 중인 키
둘 중 하나라도 있으면 "있다" — 진행 중 put 도 취소해야 하니까.
③ lock 확인
exists_many 에서 lock=True 로 걸어둔 L2 lock refcount 가 > 0 이면 누군가 read 중. 지금 evict 하면 데이터 깨짐.
④ lock 키는 보호 (force=False 일 때만)
raw_block_line.md:69 invariant:
delete(force=False)는 locked 슬롯 보존
⑤ 인덱스에서 제거
removed_entry: _Entry 에 슬롯 offset 정보가 있어 ⑧ 에서 사용.
⑥ 진행 중 put 취소
즉시 안 지우고 플래그만:
- 워커 스레드가 그 슬롯에 pwrite 중일 수 있음
- 중간에 free 처리하면 다른 put 이 그 슬롯에 덮어쓰는 race
→ 워커가 끝날 때 inflight.canceled 보고 자기 손으로 free list 로 돌려놓음.
⑧ 슬롯을 free list 로 회수
- offset → 슬롯 번호 변환
_free_slots에 추가- 다음
put이_allocate_slot_locked에서 꺼내서 재사용
중요: 디스크에는 아무것도 안 씀.
슬롯 자체에 즉시 쓰기 X — free list 에 회수만 됨, 다음 put 이 덮어씀 (
raw_block_line.md:191)
NVMe TRIM 같은 디바이스 회수 명령도 현재는 안 보냄 — raw_block_line.md:228 H6 후보 (FDP/HC-SSD 작업 시 추가).
⑨ 메타 dirty 카운터 증가
백그라운드 체크포인트 스레드가 이 값 보고 "변화 쌓였으니 메타 저장" 판단.
⑩ 결과 기록
진짜 뭔가 했을 때만 True. 없는 키 삭제하라고 했으면 False.
4 가지 케이스
| 키 상태 | force=False | force=True |
|---|---|---|
| 인덱스에 있음, lock 없음 | ✅ 삭제 → True | ✅ 삭제 → True |
| 인덱스에 있음, lock 있음 | ❌ 보존 → False | ⚠️ 강제 삭제 → True |
| inflight 만 있음 | ✅ 취소 플래그 → True | ✅ 취소 플래그 → True |
| 어디에도 없음 | ❌ → False | ❌ → False |
delete 가 안 하는 일
| 안 하는 것 | 왜 |
|---|---|
| 디스크 zero-fill | 다음 put 이 덮어씀, IO 낭비 |
| NVMe TRIM | 현재 미구현 (FDP 작업 후보) |
| 메타 체크포인트 즉시 flush | dirty 카운터만 올리고 백그라운드에 위임 |
| inflight 직접 cancel | 플래그만 세움, 워커가 마무리 (race 방지) |
한 문장
delete_many= 인덱스 제거 + 슬롯을 free list 로 회수 + 진행중 put 은 취소 플래그 + lock 키는 force=False 면 보호 — 디스크 안 건드리고 인메모리 자료구조만 정리하는 MP eviction 핵심.