Skip to content

searchBlocks: hybrid Math.max(...docs) hard-crashes at ~160K docs; docFeatures 8MB cap re-tokenizes large corpora on every call #227

Description

@ranxianglei

来源: Tyan66666/billion-context-dsh#133 分析 search_context 长会话劣化(190 块 / 13.3M 遮蔽 token 会话,单次搜索 ~20.8 分钟)时发现

Two independent kernel-layer costs in the search path, both measured on acp-kernel 0.0.29 (the billion-context-dsh host session from #133 + local reproduction at the reported scale).

1. hybridAlgorithm.score hard-crashes at large doc counts (P1)

src/search/algorithms/hybrid.ts normalizes the two channel scores with an argument spread:

const maxBm = Math.max(...bm.map((r) => r.score), 1e-9)
const maxFz = Math.max(...fz.map((r) => r.score), 1e-9)

The spread throws RangeError: Maximum call stack size exceeded once the doc count exceeds the JS argument limit (~65K–125K depending on the V8 build).

Reproduced locally with 160,170 docs (46MB text, 190 blocks): the call crashes at hybrid.ts before returning any result — the search tool THROWS instead of degrading. The #133 session (19.7K docs) was below the limit, but any session that compresses ~30K+ shadowed messages breaks search entirely.

Suggested fix: compute the max with a loop/reduce (O(n), no argument limit). Two lines.

2. docFeatures 8MB cap re-tokenizes large corpora on every call

DEFAULT_CAP_CHARS = 8 * 1024 * 1024, keyed by full doc text, evicted in insertion order (not LRU). A search corpus larger than the cap re-tokenizes (corpus − cap) on EVERY call:

  • Measured (40.4MB corpus, 20,140 docs, CJK-mixed): 17.9s per call, identical across cold/warm calls — the 8MB window slides and nothing is reused.
  • The kernel's own doc-cache docstring estimates ~0.3s/MB for the segmenter pass.
  • setDocCacheCap() exists and the host now uses it (128MB → 9.0s cold + 0.14–0.16s warm), but the cap design is a multi-session-server tradeoff: steady-state feature heap is ~4–6.3× cached source chars (measured), so a single-session host gets full-corpus caching only at ~800MB+ heap for a 70MB corpus.

Suggested directions (design call for the kernel owner):

  • key by doc ref with per-session / per-doc-set cache lifetime, so a single-session host can cache the whole corpus without a global char cap;
  • or LRU eviction so repeated calls reuse the most-touched docs.

Note: the hybrid double pass (BM25 + fuzzy both scan all docs, then applyRoleWeight/buildResults each build a full Map) is fine once features are warm (~140ms at 20K docs) — no action needed unless profiling shows otherwise.

Data

  • fix: size-aware emergency truncation (billion-context-pi#12) #133 live session (host 0.1.2-rc.1, plugin v0.2.19): 8 paired tool calls — 6 blocks → 1.8s … 190 blocks / 13.3M shadowed tokens → 1249s; 19.7K docs / ~50MB corpus per call.
  • Local reproduction (this report): N=20,861 events / B=190 / 40.4MB corpus: searchBlocks 17.9–18.0s ×3 under the 8MB cap; 9.0s cold + 0.14–0.16s warm under a 128MB cap. 160,170-doc corpus: crash (item 1).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions