quickwit-oss / quickwit-oss/quickwit
Delete task (DeleteAndMerge) on a large split causes unbounded memory (RSS) growth → OOM crashloop
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 11.7k
- Forks
- 597
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 37
Description
Version: 0.8.0 (x86_64-unknown-linux-gnu)
Backend: GCS object storage, PostgreSQL metastore, janitor running only the delete-task service.
Summary
A single delete-by-query that matches only a handful of documents triggers a DeleteAndMerge on every split containing a match. For large splits (~11M docs / ~17 GB on disk, ~29 fast fields), the merge completes successfully, but memory then grows unbounded after merge-operation-success until the process is OOM-killed. It reproduces at every memory limit we tried (1 GiB, 14 GiB, and 40 GiB), so it is not a sizing problem — the working set grows past whatever ceiling is set.
Smaller splits in the same delete task (≤~4.6M docs) finalize fine; only the large ones (≥~6.7M docs) blow up.
Repro
- Index with many fast fields (~29) and large merged splits (
split_num_docs_target = 10_000_000; observed splits up to ~11.3M docs / ~17 GB). - Submit a delete-by-query matching a few docs spread across several splits.
- Janitor delete-task service plans one
DeleteAndMergeper affected split. - On the large splits: merge logs
merge-operation-success, then RSS climbs steadily to the cgroup limit and the pod is OOM-killed. On restart it re-plans the same splits → crashloop. Each cycle also leaves orphanedStagedsplits behind (uploaded/staged but never published).
Observed (instrumented)
- RSS (anonymous heap), not page cache, is what grows — confirmed via cgroup/cAdvisor (
container_memory_rssclimbs from ~4 GB to ~38 GB under a 40 GB limit, then OOM). ~2 CPU cores pegged throughout (compute-bound, not I/O). - The growth happens after
merge-operation-successis logged, i.e. in the post-merge packaging/finalize phase, and nopublish-new-splitsis ever logged for the large splits. - We ruled out the obvious bounded allocations by reading the 0.8.0 source: split upload streams from disk in bounded chunks; hotcache is sparse; tag extraction bails above
MAX_VALUES_PER_TAG_FIELD. None scales with split size unboundedly — so the RSS growth appears to be an accumulation/leak in the delete-merge finalize path.
Impact
- A trivial delete (a few docs) can wedge the janitor indefinitely on any index that has large splits, and orphans
Stagedsplits in object storage on every crash cycle. - Workaround: disable the delete-task service and keep
split_num_docs_targetsmall so delete-merges only ever operate on small splits — but this does not resolve already-large splits, which only clear via retention/reindex.
Questions for maintainers
- Is this a known issue / is there a fix in a later release?
- Is the post-merge memory for a delete-merge expected to scale with split size (and if so, roughly how)? A bound proportional to split size would at least make sizing predictable; the unbounded growth we see suggests a leak.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce a delete-by-query on a large split with the janitor delete-task service, then trace DeleteAndMerge after the merge-operation-success log through post-merge packaging and finalize. Check why publish-new-splits is never reached and why staged splits accumulate; done means memory remains bounded and the affected splits publish without orphaned Staged splits.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- google-cloud, postgresql, rust
- Domain
- backend, cloud, databases
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100