Lance fragment 무한 누적 + 파일당 삭제 커밋 — 대량 삭제 시 ingest 32시간 stall #230
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
증상
대량 삭제가 발생한 워크스페이스에서
kebab ingest가 진행바0/12115상태로 약 32시간 동안 ingest 루프에 진입하지 못한다. 문서 1건 purge 에 20초가 걸리고, 그 시간 전부가 삭제와 무관한 오버헤드다.kebab reset --vector-only로 Lance 를 비우면 같은 작업이 문서 1건당 0.17초로 떨어진다 — 약 120배 차이. 즉 비용은 삭제할 벡터의 개수가 아니라 Lance 데이터셋의 누적 상태에서 나온다.원인 (세 겹)
1. 삭제가 파일 1건당 Lance 커밋 1회
sweep_deleted_files는 루프 안에서 파일마다delete_by_chunk_ids를 호출한다 (crates/kebab-app/src/lib.rs:2101-2113).execute_orphans_only도 동일 구조 (crates/kebab-app/src/reset.rs).delete_by_chunk_ids는 200개 배치로table.delete(predicate)를 실행하므로 (crates/kebab-store-vector/src/store.rs:288-347), 평균 5 chunk 짜리 문서에서는 결국 문서 1건 = Lance 커밋 1회가 된다. 삭제 대상 5,834건 → 커밋 5,834회.빈 테이블에서도 이 커밋은 그대로 발생한다.
reset --vector-only직후 재실행한 sweep 이 10분 동안_versions/_transactions에 각각 3,915개 파일을 만들었고, 같은 구간에서 purge 된 문서 수는 3,965건이었다 (1:1 대응).2. fragment 가 무한 누적 — compaction 호출이 없다
upsert는 asset 1건당merge_insert_batch를 1회 호출한다 (crates/kebab-store-vector/src/store.rs:256). Lance 는 이때 fragment 를 하나 만든다. 코드베이스 전체에optimize/compact_files/cleanup_old_versions호출이 없다.도그푸딩 인스턴스 실측 (문서 11,229건):
data/)_versions/총 크기data/)메타데이터가 실데이터의 2.5배다. manifest 크기가 코퍼스에 비례해 커지므로 모든 쓰기 경로가 함께 느려진다.
3. lance 가 fragment 마다 DataFusion planner 를 새로 만든다
table.delete(predicate)는 fragment 전체를 스캔하는데,FilteredReadStream::read_fragment가 fragment 마다Planner::new를 호출하고 그 안에서LanceContextProvider::default→register_functions→geodatafusion::register가 실행된다. geo(지리정보) UDF 와 Arrow geometry 스키마 전체를 fragment 11,892개마다 처음부터 재구성한다.sample <pid>4초, 메인 스레드 100%:kebab 은 geo 기능을 쓰지 않는다.
lancedb = { version = "0.23", default-features = false }인데도lance-datafusion이geodatafusion 0.1.1을 무조건 끌어온다 (lancedb 0.23.1 / lance 1.0.1).실측 요약
reset --vector-only후)제안
sweep_deleted_files/execute_orphans_only에서 chunk_id 를 전부 모아 루프 종료 후delete_by_chunk_ids를 1회 호출. 커밋 5,834회 → 30회(200개 배치). SQLite purge 는 건별 커밋을 유지해야 하므로(중단 안전성) 벡터 삭제만 뒤로 미루고, 실패 시 orphan 벡터가 남는 현재 정책은 그대로 둘 수 있다.optimize()/compact_files()+cleanup_old_versions()호출. 지금은 색인을 오래 쓸수록 단조적으로 나빠지고, 사용자가 회복할 수단이reset --vector-only(전량 재임베딩) 밖에 없다.doctor에 fragment 수 / manifest 크기 / 버전 수 지표를 노출. 지금은 사용자가 이 상태를 알 방법이 없다.관련
chunks_fts삭제 전체 스캔 (별도 이슈) — Lance 를 비운 뒤 남은 6건/초 상한의 원인PR #233(압축 정책) + PR #234(삭제 배치화 / 삭제 경로 압축 / doctor 지표)로 네 제안을 모두 반영해 닫는다.
실측 근거는 두 PR 본문과
tasks/HOTFIXES.md2026-08-16 항목에 있다. 요약하면 2.8만 문서 색인 중 속도가 분당 32~36건으로 평평하게 유지됐고(수정 전 30.7 → 4.3건 단조 하락), LanceDB 크기는 문서가 1.7배 늘었는데 15 GB에서 2.8 GB로 줄었다. 문서 364건 삭제가 만드는 Lance 커밋은 33회에서 1회가 됐다.이슈 본문 중 두 가지는 실측과 달랐다. 회복 수단이
reset --vector-only전량 재임베딩뿐이라고 적혀 있었으나, 기존 테이블 압축 한 번이 153초에 15 GB를 1.7 GB로 되돌린다(행 수 불변). 그리고 메타데이터 대 실데이터 비율은 11,229 문서에서 2.5배, 16,828 문서에서 7.2배로, 제곱으로 증가한다는 것이 두 지점에서 확인됐다.