From c126ed6d76fd774e02a32d9a0c4bca6cc56a86a3 Mon Sep 17 00:00:00 2001 From: wefio <48851810+wefio@users.noreply.github.com> Date: Fri, 30 Oct 2026 19:08:17 +0800 Subject: [PATCH 1/2] fix(onlion): only the big wasm is deferred; callbacks.wasm stays a module f86d3844 shipped a defect: the inline-wasm plugin matched every *.wasm, so callbacks.wasm became a factory function too. The generated loader imports it as a real WebAssembly.Module and does `new WebAssembly.Instance(callbacksModule, ...)`, which threw "Argument 0 must be a WebAssembly.Module" inside runSQLite3PostLoadInit - every /search that needed SQLite answered 503 in 75-150 ms, while /add kept succeeding (the accept never touches SQLite). callbacks.wasm (19 KB) is back to an eager module; sqlite3.wasm (the 1.5 MB one, the actual cold-start cost) stays behind the lazy factory. Caught on the deployed endpoint, not by the suite: Miniflare exercises the wrangler build, which never runs this plugin. `npm run test:edgeone` does drive the built bundle and does load SQLite, so it covers this class. --- onlion/scripts/build-edgeone.mjs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/onlion/scripts/build-edgeone.mjs b/onlion/scripts/build-edgeone.mjs index d07bd00f..75712a79 100644 --- a/onlion/scripts/build-edgeone.mjs +++ b/onlion/scripts/build-edgeone.mjs @@ -54,6 +54,18 @@ const inlineWasm = { name: "inline-wasm", setup(pluginBuild) { pluginBuild.onLoad({ filter: /\.wasm$/ }, async (args) => { + // Only the big one is deferred. `callbacks.wasm` (19 KB) is consumed by the generated + // loader as a real WebAssembly.Module via a static import, so wrapping it in a factory + // made `new WebAssembly.Instance(function, ...)` throw and every SQLite request answer 503 + // - caught on the deployed endpoint, not by the suite (Miniflare uses the wrangler build). + if (path.basename(args.path) === "callbacks.wasm") { + const bytes = fs.readFileSync(args.path).toString("base64"); + return { + loader: "js", + contents: `export default new WebAssembly.Module(Buffer.from(${JSON.stringify(bytes)}, "base64"));`, + watchFiles: [args.path], + }; + } const bytes = fs.readFileSync(args.path).toString("base64"); const encoded = JSON.stringify(bytes); return { From bff639fd906b893c94ff08a6711a93cdf85b6e69 Mon Sep 17 00:00:00 2001 From: wefio <48851810+wefio@users.noreply.github.com> Date: Fri, 30 Oct 2026 19:12:33 +0800 Subject: [PATCH 2/2] perf(onlion): a search loads SQLite only when it has a partition to read, and stops retrying OPFS Two behaviour-preserving transforms, both measured: 1. search loaded SQLite before reading anything (worker.mjs, `sqliteReady()` in search). A user whose writes are still in the log has no partitions at all - the harness's first questions are exactly that - so the module was decoded and compiled for nothing. `readSearchCandidates` now takes a memoised `getSqlite` and resolves it inside the partition loop. Verified: a search on an unknown user returns 200 in 19 ms with `sqlite_loaded: false`. 2. every SQLite load tried to install the OPFS VFS twice and threw twice (no `location` in this runtime), printing two stack traces per cold start. The loader gates those installers on `config.disable.vfs`, whose source is `globalThis.sqlite3ApiConfig` (worker-loader.mjs 3064, gates at 15699 and 16964) - not init()'s argument, which is why passing it there changed nothing. Verified: the two stacks per load went from 2 to 0 in a full verify run (22 occurrences before, 0 after). Full suite 37 PASS. --- onlion/backend/worker.mjs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/onlion/backend/worker.mjs b/onlion/backend/worker.mjs index d65857ad..698f6175 100644 --- a/onlion/backend/worker.mjs +++ b/onlion/backend/worker.mjs @@ -174,6 +174,16 @@ async function sqliteReady() { // not pay for decoding and compiling them. This platform starts a fresh isolate per request, // so that saving is per request, not per container. const compiled = typeof compiledSqlite === "function" ? compiledSqlite() : compiledSqlite; + // OPFS is a browser storage VFS: this runtime has no `location`, so both installers throw a + // TypeError and the loader prints a stack on every cold start - the log noise the operator has + // seen since day one. The loader gates them on `config.disable.vfs` (worker-loader.mjs 15699, + // 16964), and its config comes from `globalThis.sqlite3ApiConfig` (worker-loader.mjs 3064), + // **not** from init()'s argument: passing it to init() had no effect at all, which is why this + // is set here instead of with the other options. + globalThis.sqlite3ApiConfig = { + ...(globalThis.sqlite3ApiConfig ?? {}), + disable: { ...(globalThis.sqlite3ApiConfig?.disable ?? {}), vfs: { opfs: true, "opfs-wl": true } }, + }; const instance = await init({ instantiateWasm(imports, receive) { const instance = new WebAssembly.Instance(compiled, imports); @@ -1306,7 +1316,7 @@ function addClauseContributions(contributions, clauses) { * own loop with its own deadline branches. It mutates `contributions` and `appendedById` * in place because the attribution across the whole request (all partitions plus the * pending entries) is one set of totals, not a per-partition stat. */ -async function readSearchCandidates(env, sqlite, { manifest, queries, topK, appendOnly, features, client, started, contributions, appendedById }) { +async function readSearchCandidates(env, getSqlite, { manifest, queries, topK, appendOnly, features, client, started, contributions, appendedById }) { const modes = new Set(); let candidates = []; let partial = false; @@ -1322,7 +1332,7 @@ async function readSearchCandidates(env, sqlite, { manifest, queries, topK, appe const result = await runPart( env.MEMORY, manifest.parts[i], - sqlite, + await getSqlite(), async ({ key: partKey }) => { // Declining the backfill is what makes this read safe and cheap: without it the // service would embed vectorless records inside a search and change the SQLite @@ -1387,7 +1397,13 @@ async function search(env, input) { let manifest = (await measure("manifest_read", () => loadManifest(env.MEMORY, keyFor(userId), userId), )).manifest; - const sqlite = await measure("sqlite_init", () => sqliteReady()); + // Loaded on the first partition, not up front: a user whose writes are still in the log has no + // partitions at all (the harness's first questions are exactly that), and the manifest read is + // the only thing a search needs for them. Measured before this: /health at 25 ms with + // sqlite_loaded=false against 84 ms for a search that loaded the module for nothing. + let sqlitePromise; + const getSqlite = () => + (sqlitePromise ??= measure("sqlite_init", () => sqliteReady())); for (;;) { try { { @@ -1395,7 +1411,7 @@ async function search(env, input) { await warmClauseVectors(client, features, manifest.parts); const persistence = { inline_delta_saves: 0, full_snapshot_saves: 0, changed_pages: 0 }; const { candidates: pageCandidates, modes: modesRead, partial: partialRead } = - await readSearchCandidates(env, sqlite, { + await readSearchCandidates(env, getSqlite, { manifest, queries, topK,