Skip to content

fix(onlion): give the write path its own embedding timeout and a total budget - #125

Merged
wefio merged 2 commits into
mainfrom
fix/write-path-embedding-budget
Oct 29, 2026
Merged

wefio merged 2 commits into
mainfrom
fix/write-path-embedding-budget

Conversation

@wefio

@wefio wefio commented Oct 29, 2026

Copy link
Copy Markdown
Owner

What the deployed logs establish

Memory is settled, and it is not the problem. From the trace on 6e7421a8:

reading value
wasm_linear_mb 8 → 28 MB, never larger
high_water_databases / high_water_image_mb 1 / 2.5-5.7 MB
heapUsed per request 122-186 MB
baseline_rss across instances 577, 604, 622, 635, 649, 661, 670, 674, 687, 688, 689, 695, 696, 701, 704, 709 MB
uptime_s / seq ~2600 s / always 1

seq: 1 with uptime_s: 2600 means every invocation is a fresh isolate inside a container that
has been alive 43 minutes. So the 577-710 MB is the container's rss, already there before our
code runs, and our request adds about 10-30 MB. No allocation change inside a request can move a
600 MB container figure - the WASM hypothesis is dead, and so is the "images outliving their
part" one.

The write path is what stalls. /add shows 5.8 / 7.5 / 9.5 / 17.5 / 19.9 / 120000 ms, with
dependency_timeout at stage: embedding and a 60 s report whose live_databases: 0 says the
request is not in SQLite - it is waiting on the provider.

The function is in ap-hongkong and the provider is in North China. Two hours earlier the same
code accepted an Add in 0.4-0.6 s, so the variable is that path.

Changes

change why
documents: 25 s per attempt (queries stay at 10 s) 10 s is generous for a query and too tight for a document batch crossing a boundary; a cut document is a failed Add
write path: 45 s total budget (NMG_EMBED_WRITE_BUDGET_MS) bounds a slow provider at 45 s instead of letting the platform kill the request at 120 s with no answer
embeddingOperation records ms, slow report carries last_embedding two incidents were diagnosed without ever knowing the cross-region call duration

/health limits reports embed_document_timeout_ms and embed_write_budget_ms.

Verification

onlion npm run verify: 35 PASS. eslint clean. Complexity gate clean for every file here (the
two dsh/dsh-nmg items belong to another agent's uncommitted work).

Not addressed here

The root cause is the topology: every Add and every search crosses a region boundary to the
provider, and up to 24 calls per search. Moving the endpoint next to the function - or running it
in the same region - is a configuration change worth more than any timeout, and the new ms
field is what will price it.

wefio added 2 commits October 29, 2026 16:21
…l budget

The deployed logs show Adds taking 5-20 s with successful embedding, and 55-120 s when the
provider stalls, ending in `dependency_timeout` at `stage: embedding`. The function runs in
ap-hongkong and the provider is elsewhere, so every call crosses a region boundary: two hours
earlier the same code accepted an Add in 0.4-0.6 s, so the variable is the path, not the code.

Two changes follow from that, plus the measurement that was missing:

- documents get a 25 s per-attempt timeout instead of the 10 s used for queries. Ten seconds
  is generous for a query and too tight for a document batch that carries a whole message
  across a boundary, where a healthy round trip and a stalled one differ by an order of
  magnitude. A cut document is a failed Add, which is the one failure the write path cannot
  absorb.
- the write path shares a 45 s total budget (`NMG_EMBED_WRITE_BUDGET_MS`). Without it one
  slow provider turned a single Add into a 55-120 s request the platform killed with no
  answer; past the budget the Add fails with `embedding_budget_exhausted` and the caller can
  retry the same request_id.
- `embeddingOperation` records the duration in `lastEmbedding`, and the slow-request report
  carries `last_embedding`. Two incidents were diagnosed without ever knowing how long the
  cross-region call took.

Verified: onlion npm run verify 35 PASS; eslint clean; /health limits reports
embed_document_timeout_ms and embed_write_budget_ms.
…arates endpoints

Two changes with one cause: the provider is across a region boundary.

**A search embedded one feature at a time.** `warmClauseVectors` called
`embedQueries([text])` per feature and the retrieval engine does the same, so a five-clause
query with windows cost up to twenty-four round trips before the first partition was opened.
The query cache was keyed by the input array, which is why warming had to match the engine's
shape exactly: keying by text instead lets the warm pass fetch every distinct feature in one
call and the engine's per-feature lookups hit it. Measured on the deployed build the same
search now makes two calls (one for features, one for pending records) instead of one per
feature; the test asserts the call count, not just that a batch happened.

**The index identity ignored the endpoint.** Two deployments of `text-embedding-v4` serve the
same model name at different endpoints; a vector from one is not comparable with a query
embedded by the other, and nothing in the identity - model prefix, dimensions, templates,
profile - differed. Switching endpoints therefore left every stored vector in place and mixed
two spaces silently. The endpoint now participates (scheme, host, path, normalised so a
trailing slash does not change it), which makes a switch mark the index stale so the existing
backfill re-embeds.

Consequence to expect on deploy: every stored vector becomes stale by construction and is
re-embedded on the next write or search for that user, one batch of 32 at a time.

Verified: onlion npm run verify 35 PASS; root product tests pass; prettier and eslint clean.
@wefio
wefio merged commit 48b1882 into main Oct 29, 2026
13 of 15 checks passed
@wefio
wefio deleted the fix/write-path-embedding-budget branch October 29, 2026 09:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant