본문으로 건너뛰기

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=Falseforce=True
인덱스에 있음, lock 없음✅ 삭제 → True✅ 삭제 → True
인덱스에 있음, lock 있음보존 → False⚠️ 강제 삭제 → True
inflight 만 있음✅ 취소 플래그 → True✅ 취소 플래그 → True
어디에도 없음❌ → False❌ → False

delete 가 안 하는 일

안 하는 것
디스크 zero-fill다음 put 이 덮어씀, IO 낭비
NVMe TRIM현재 미구현 (FDP 작업 후보)
메타 체크포인트 즉시 flushdirty 카운터만 올리고 백그라운드에 위임
inflight 직접 cancel플래그만 세움, 워커가 마무리 (race 방지)

한 문장

delete_many = 인덱스 제거 + 슬롯을 free list 로 회수 + 진행중 put 은 취소 플래그 + lock 키는 force=False 면 보호 — 디스크 안 건드리고 인메모리 자료구조만 정리하는 MP eviction 핵심.