앞 PR(#233)이 #230 의 제안 2(압축 정책)만 닫았다. 남은 1·3·4 를 처리한다.
1) 삭제가 파일 1건당 Lance 커밋 1회 (제안 1)
`sweep_deleted_files` 와 `execute_orphans_only` 가 루프 안에서 파일마다
`delete_by_chunk_ids` 를 불렀다. 그 호출 하나가 Lance 커밋 하나라 문서
삭제 수와 커밋 수가 1:1 이었다 — #230 이 보고한 "삭제 5,834건 → 커밋
5,834회" 가 이것이다.
chunk_id 를 sweep 전체에 걸쳐 모아 루프가 끝난 뒤 한 번만 부른다. SQLite
purge 는 건별 커밋을 유지한다. 그쪽이 crash-safety 경계이고, 기존 정책이
이미 중간에 죽으면 orphan 벡터가 남는 것을 허용한다.
실 KB 실측(28,427 문서, 나무위키 샤드 하나 = 364 문서 삭제):
Lance 커밋 364 → 33, 버전 번호 28474 → 28507, _transactions 336 → 369.
33 은 `delete_by_chunk_ids` 의 200개 배치 상한에서 나온다(364 문서 ×
약 18 청크 ≈ 6,500 id / 200). 커밋 수가 문서 수가 아니라 청크 수에
비례하게 됐다.
2) 삭제 경로에도 압축
압축이 upsert 에만 걸려 있어 `reset --orphans-only` 처럼 삭제만 하는
경로는 여전히 무한 누적이었다. 트리거를 `maybe_compact` 헬퍼로 빼고
`delete_by_chunk_ids` 끝에서도 부른다.
3) doctor 에 Lance 지표 (제안 4)
`vector_store` 체크 추가 — fragment 수 / 데이터 크기 / 버전 수 /
메타데이터 크기를 보여주고 메타데이터가 데이터보다 크면 fail 로 잡는다.
그게 #230 병리의 모양이다. lancedb 를 열지 않고 파일시스템만 읽어서
임베딩 provider 가 none 이어도 나오고 비용이 0 이다. `DoctorCheck` 목록에
항목을 더하는 것이라 `doctor.v1` wire 는 그대로다.
4) geodatafusion (제안 3) 은 미해결
lance 내부라 kebab 쪽 feature flag 가 없다. 다만 이 비용은
`table.delete()` 호출당 fragment 수만큼 곱해지므로 위의 커밋 수 감소가
그대로 곱셈 횟수 감소다. 근본 해결은 upstream 이슈.
부수 관찰: 364 문서 삭제에 38.7분이 걸렸는데 Lance 쪽은 33 커밋뿐이므로
남은 비용은 #229(chunks_fts 삭제가 FTS5 전체 스캔, 문서당 약 6초)다.
364 × 6초 ≈ 36분으로 거의 전부 설명된다. #230 을 고쳐도 sweep 체감이
안 바뀌는 이유가 이것이다.
검증: 워크스페이스 186 결과 전부 통과. 신규 회귀 테스트
`repeated_deletes_do_not_accumulate_lance_versions` 는 삭제 경로 압축을
빼면 40회 삭제에 매니페스트 42개로 실패한다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017c9JwQq8ZkGvYjpKXMiDhF