docir — design documents Graph

Documents / Issues / issue-e9ef984d5ec7

The embedding model takes every core, so warming, reindexing and searching saturate the CPU

fastembed builds its ONNX session with no thread limit unless one is passed; DOCIR_EMBED_THREADS caps it, and the daemon respawns when it changes.

issue-e9ef984d5ec7issueresolved
View as Markdown◉ View in graph

What is reported

docir saturates the CPU. The reporter asks for a way to run it with a chosen number of threads, and names the cause as the vector-search functionality: warming the model, reindexing, and searching.

What is behind it

One thing, on three paths. fastembed builds its ONNX session with no thread limit unless one is passed, so the model takes every core it can see — while the model warms, while reindex embeds a corpus, and while each context or search query is embedded. Nothing else in docir spawns workers.

TextEmbedding(threads=N) sets both intra_op_num_threads and inter_op_num_threads, so one number covers all three paths.

Resolution

FIXED — DOCIR_EMBED_THREADS caps it.

An environment variable and deliberately not a schema key: a store is a committed artifact read by whoever clones it, so a core count inside it would impose one machine's hardware on the whole team. That is Where a downloaded embedding model lives's argument for keeping the model out of the store, one field over.

Unset changes nothing, so no machine gets slower by upgrading. An unusable value (zero, negative, not a number) reads as unset rather than raising: this is read while every container is built, docir doctor included, and a typo in a shell profile must not break the command somebody runs to diagnose the problem.

docir doctor reports the cap under embedding.threads, absent when uncapped.

The trap that had to be closed with it

The daemon resolves the cap once, when it builds its embedder, and answers every later request from that. So setting the variable changed nothing until the daemon idled out — while doctor, which reads the client's environment, reported the new value the whole time. A setting whose own diagnostic lies about whether it is in force is worse than no setting.

The cap therefore joins the code stamp and the schema digest in the pid file, as a third staleness input: a daemon spawned with a different cap is stopped and replaced, exactly as one serving stale code is. An unusable value maps to None on both sides, so a typo does not respawn the daemon once per command.

Still open: the default

Unset remains fastembed's own behaviour — every core. That is the right default on a build machine and the reported complaint on a laptop, and changing it would silently alter throughput for everyone. Measured on this repository's 250-document corpus before deciding; the numbers are on GitHub #23.

To amend: Re-verify: