feat(open-path): init never gates on the embedding model; open goes concurrent; slow opens narrate
A production restart storm measured 90,017ms for a single brain init vs 1,117ms quiet (~80x contention multiplier), traced to performInit() eagerly awaiting the process-global WASM embedding engine before the VFS root even existed. Every writer's open() queued on the one throttled model compile (90-140s on throttled CPUs). - VirtualFileSystem.doInitializeRoot() no longer embeds '/'. The root is system-tier plumbing nothing ever searches; when the default WASM engine is active it now gets an explicit all-zero placeholder vector (cosineDistance returns max distance for a zero vector, so it never ranks ahead of real content). deferEmbedding was considered and rejected: its landing path kicks the embed worker synchronously right after commit, which would still force the cold compile within milliseconds — just off the awaited path, not avoided. A registered native 'embeddings' provider (no cold-start cost, possibly a different dimension) still embeds the root for real, via the new Brainy.usesDefaultWasmEmbedder() seam. - performInit()'s eager-embedding step now only STARTS the WASM engine warm in the background instead of awaiting it inline. embed()/embeddingManager already serialize concurrent callers on one shared init promise, so the first real embed() converges correctly either way; a failed warm narrates loudly instead of surfacing as a silent latency spike or an unhandled rejection. eagerEmbeddings: false still means no warm at all. - FileSystemStorage.init() batches its ~8 independent bootstrap mkdirs (each creates its own full subtree via recursive:true, so none depend on the others existing) into one Promise.all. The restore-completion step and initializeCounts() stay strictly sequential — they have real order dependencies on rootDir and systemDir respectively. - performInit() now times five phases (storage init / generation-store open+fold / index init+gate / VFS bootstrap / embedding-warm-started) and logs one warning with the per-phase breakdown when total open exceeds 2000ms; silent otherwise.
This commit is contained in:
parent
f8f64780b1
commit
96624f408c
5 changed files with 446 additions and 53 deletions
|
|
@ -1983,25 +1983,32 @@ export interface BrainyConfig {
|
|||
reservedQueryMemory?: number // Memory reserved for queries in bytes (e.g., 1073741824 = 1GB)
|
||||
|
||||
/**
|
||||
* Controls when the WASM embedding engine is initialized.
|
||||
* Controls whether `init()` starts a BACKGROUND warm of the WASM embedding
|
||||
* engine.
|
||||
*
|
||||
* **Adaptive default (8.0):** when omitted, the engine eagerly initializes
|
||||
* during `init()` whenever the WASM embedder is the *active* one — i.e. no
|
||||
* native `'embeddings'` provider is registered — and this instance is a
|
||||
* writer (not `mode: 'reader'`) running outside unit tests. The WASM module
|
||||
* (≈93MB with the embedded model) takes 90-140s to compile on throttled
|
||||
* CPUs, so paying that during boot rather than on the first `embed()`-driven
|
||||
* call is the right default for a single-process server.
|
||||
* **Adaptive default (8.0, background since the open-path fix):** when
|
||||
* omitted, `init()` STARTS a background warm of the engine whenever the
|
||||
* WASM embedder is the *active* one — i.e. no native `'embeddings'`
|
||||
* provider is registered — and this instance is a writer (not
|
||||
* `mode: 'reader'`) running outside unit tests. The WASM module (≈93MB with
|
||||
* the embedded model) takes 90-140s to compile on throttled CPUs — but
|
||||
* `init()` never awaits that compile. It only starts it, so N concurrent
|
||||
* opens no longer serialize on the one process-global engine singleton.
|
||||
* The first `embed()` call then waits for whichever finishes first: the
|
||||
* background warm (if still running) or its own fresh init (if the warm
|
||||
* never started, e.g. `eagerEmbeddings: false`) — both paths converge on
|
||||
* the SAME shared promise inside the engine singleton, so the vector is
|
||||
* always correct; only the timing of who pays the wait differs.
|
||||
*
|
||||
* The adaptive path skips itself automatically when a native embeddings
|
||||
* provider owns embeddings, in reader-mode (readers query existing vectors
|
||||
* and never embed), and in unit-test mode (kept fast via the mock embedder).
|
||||
*
|
||||
* - `true` — force eager init during `init()` (the adaptive default already
|
||||
* does this for the active-embedder writer case; set it explicitly to be
|
||||
* unambiguous).
|
||||
* - `false` — explicit override to force lazy init (first `embed()` call)
|
||||
* even when this instance is the active embedder.
|
||||
* - `true` — force the background warm to start during `init()` (the
|
||||
* adaptive default already does this for the active-embedder writer
|
||||
* case; set it explicitly to be unambiguous).
|
||||
* - `false` — no warm at all. Fully lazy: the first `embed()` call pays the
|
||||
* full cold-compile cost inline, on whichever request triggers it.
|
||||
*/
|
||||
eagerEmbeddings?: boolean
|
||||
|
||||
|
|
|
|||
Reference in a new issue