GrooveSeek

Semantic search over a Markdown knowledge base, served over MCP.

View the Project on GitHub alphabet-h/grooveseek

26. 索引のサイズ上限を設定可能にし、無制限は綴らせ、読み出し上限は据え置く

背景と課題

この記録までは、1 つの定数 MAX_RAW_BINARY_BYTES (50 MiB、grooveseek/src/parser/mod.rs) が 6 つのことを同時に決めていた: indexer がバイナリファイルを読む前に skip するサイズ、 読む handle での同じ判定、.xlsx の展開 pre-flight の budget、.docx / .pptx のパート 読み出しの累積 budget、PDF 1 本のテキスト budget、そして get_document / resources/read のバイナリ上限である。MAX_RAW_TEXT_BYTES は最初の 2 つを Markdown とプレーンテキストに ついて持っていた。

300 MB の workbook は普通の業務に存在し、warning 付きで skip されていた。上限を全員に 対して上げるだけでは済まない: rebuild_index はクライアントがいつでも呼べる MCP ツールで、 ファイルは parse の間まるごとメモリに持たれ、calamine は .xlsx の共有文字列表も丸ごと メモリに載せ、しかも確保に失敗すると process が abort する。abort は catch_unwind でも 別スレッドでの parse でも止まらない。

問いは、設定で求めていない構成には何も変えずに、運用者がそうしたファイルを分かって 受け入れられるようにする方法である。

判断の軸

検討した選択肢

  1. 「無制限」の綴り。
    • 0。却下: 製品ごとに意味が違う。Recoll は filtermaxseconds では無制限、 compressedfilemaxkbs では「全拒否」と読み、Zoekt は「既定を使う」と読み、Open WebUI は 未設定または 0 の RAG_FILE_MAX_SIZE を、同じ製品の中でアップロードでは無制限、 アーカイブ展開では 100 MB として扱う。この repository は既に [embedding].max_input_chars = 0 を拒否している。
    • -1。最も多い選択 (Elasticsearch indexed_chars、Apache Tika、Solr)。却下: TOML 上で 負のバイト数は意図として読めず、「では -2 は?」を招く。
    • キーを書かなければ無制限。却下: 調べたサーバ型の製品 (Onyx、Dify、Meilisearch、 Qdrant、Weaviate、Nextcloud) はすべて未設定を有限の既定値として扱う。
    • 文字列 "unlimited"。 採用。0 と負値はエラーにし、メッセージでこれを案内する。
  2. 展開 budget。
    • raw の上限に連動させる。却下: workbook の中の XML はファイルの数倍に展開されるので、 max_binary_file_size = "300 MiB" でも 300 MB の workbook は展開段で skip され、 通す方法が "unlimited" しか残らず、それは全ファイルから zip-bomb の検査を外す。
    • raw が "unlimited" の時だけ連動させる。却下: 「無制限」が 2 つ目の防御を黙って外す。
    • Apache POI (最小展開比 1%) や Tika (出力:入力比 100) のように比率で縛る。今回は却下: 既存の pre-flight は絶対バイト数を 2 層 (申告値、実際の展開量) で数えており、軸を 変えるのは別の判断である。
    • 独立したキー max_decompressed_size、既定 50 MiB、raw の上限と非連動。 採用。
  3. 読み出しが返すもの。
    • get_document / resources/read を索引の上限に連動させる。却下: MCP の 1 リクエストが 300 MB のファイルをメモリに持つことになる。
    • 読み出し上限は据え置く: バイナリ形式 50 MiB、テキスト 1 MiB。 採用。それを超えて 索引された文書は、索引に記録したサイズ (ADR-0005) によって、検索には出るが uri を持たない。
  4. キーの置き場所。 [limits] の新設は却下した。[index] が既に groove index と rebuild_index の設定を持ち、上限は索引の性質そのものである。

  5. バイナリとテキストを 1 キーにする。 却下: テキストには chunk の大きさの上限が無い (Markdown は見出しでしか切らない) ので、共有の上限を上げると見出しの無い .txt 1 本が 巨大な 1 chunk になり得る。

決定

結果と代償

参考