lance-format / lance-format/lance

bug: BM25 FTS query attempts ~96 GiB allocation and aborts in `InvertedIndex::bm25_search_modern_candidates` (regression vs lance 10)

Open
#9,016 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug performance
Dominant language
Rust
Stars
7.1k
Forks
852
Avg merge
3d 18h
Merged PRs (30d)
272

Description

Description

Summary

A BM25 full-text search query (no writes involved) against a jieba-tokenized inverted index triggers a single Rust allocation of 103,079,215,056 bytes (~96 GiB) in lance_index::scalar::inverted::InvertedIndex::bm25_search_modern_candidates, causing an immediate abort().

The same table + index + query works fine on lance 10 (peak RSS ~350 MB). This is a size-calculation bug (integer overflow / wrong memory estimate), not an actual OOM.

Environment

Component Working Failing
@lancedb/lancedb (node) 0.37.1 0.38.0
lance core (Rust) 10.0.0 11.0.0
jieba-rs 0.10.1 0.10.1 (same)
pylancedb 0.37.1 (lance 10) → ✅
Node.js 22.19.0, linux x64 same
Table size 1,041 rows, FTS index on one text column, vector column 768-dim same
Tokenizer jieba, custom dicts under LANCE_LANGUAGE_MODEL_HOME (dict.txt ~5 MB) same

Table and FTS index were built by the Python SDK; the node SDK only reads. (Cross-language reads are fine — the crash is purely on the FTS query path.)

Minimal repro (node)

import * as lancedb from "@lancedb/lancedb";

// env: LANCE_LANGUAGE_MODEL_HOME points to dir containing jieba/custom/dict.txt + jieba/default/dict.txt
const conn = await lancedb.connect("/path/to/db");
const table = await conn.openTable("documents");

// Pure FTS query alone is enough to crash on node 0.38.0 / lance 11
const res = await table.search("NGP渗透率", "fts").limit(5).toArray();  // ← abort here

Hybrid (search(vec, "vector").fullTextSearch(text).rerank(RRFReranker)) crashes identically — the abort happens in the FTS segment before RRF runs.

Note: the Rust abort() bypasses JS try/catch entirely and dumps core — in a memory-constrained container it presents as an abrupt OOM-kill of the whole process group, with no catchable error surfaced to JS.

Error output (with RUST_BACKTRACE=full)

[0] baseline RSS=80MB
[1] openTable RSS=90MB
memory allocation of 103079215056 bytes failed
   9: alloc::raw_vec::handle_error
  10: <lance_index::scalar::inverted::index::inverted_index::InvertedIndex>
        ::bm25_search_modern_candidates::{closure#0}::{closure#0}::{closure#1}
  11: <InvertedIndex>::bm25_search_modern_candidates::{closure#0}
  12: <InvertedIndex>::bm25_search_documents::{closure#0}::{closure#0}
  13: lance::io::exec::fts::search_segments::{closure#0}
  14: <lance::io::exec::fts::MatchQueryExec as datafusion_physical_plan::ExecutionPlan>::execute
  ...
  26: napi::tokio_runtime::execute_future_impl (node binding)

The requested allocation 103079215056 bytes is near-exactly 96 GiB (0x1800000000) — strongly suggesting an overflowed/incorrect size computation, since the actual data is ~1k rows and a 5 MB dictionary.

Control experiment (same process, same index, same query)

Path lance 10 (node 0.37.1 / py 0.37.1) lance 11 (node 0.38.0)
open table ✅ 88–90 MB ✅ 90 MB
pure FTS query ✅ 176 MB, ~0.7 s 💥 abort (~96 GiB alloc)
hybrid + RRF ✅ 246–341 MB 💥 abort (same frame)
hybrid + where filter
py SDK same query ✅ 363 MB peak

Downgrading only the node SDK to 0.37.1 (lance 10, same jieba-rs 0.10.1) makes every query pass, so the regression is in lance 10 → 11.

Expected behavior

BM25 FTS query should complete in tens of MB, as it does on lance 10.

Suspected area

lance-index InvertedIndex::bm25_search_modern_candidates (new in lance 11's "modern candidates" BM25 path) — the size passed to the allocator appears to be derived from something like doc_count × ... with a wrong/overflowed factor rather than actual posting-list sizes.

Steps to reproduce
import * as lancedb from "@lancedb/lancedb";

// env: LANCE_LANGUAGE_MODEL_HOME points to dir containing jieba/custom/dict.txt + jieba/default/dict.txt
const conn = await lancedb.connect("/path/to/db");
const table = await conn.openTable("documents");

// Pure FTS query alone is enough to crash on node 0.38.0 / lance 11
const res = await table.search("NGP渗透率", "fts").limit(5).toArray();  // ← abort here
Expected behavior

No response

Lance version

11.0.0

Language binding

Java

Environment

No response

Logs / traceback

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with lance-index's InvertedIndex::bm25_search_modern_candidates entry point and run the minimal Node reproduction against lance 11, comparing it with lance 10. Trace the allocation-size calculation for the jieba-tokenized index and confirm that the BM25 query completes without the ~96 GiB allocation or abort.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, rust
Domain
backend, search
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.