fix(query): drain cache writes before CLI exit

The query cache write was fired with `void promise.catch(...)` — true
fire-and-forget. On a fast CLI invocation (`gbrain query <q>` exits in
~50ms), the process terminates before the cache write commits. Result:
the cache effectively never warms from CLI use; every query is a miss.

`awaitPendingSearchCacheWrites()` tracks each in-flight cache write in a
module-level Set. The CLI dispatcher awaits the set after `query`
finishes formatting output but before the process exits. MCP server path
unchanged (long-lived process, fire-and-forget remains correct).

Cherry-picked from PR #1125.

Co-Authored-By: hnshah <hnshah@users.noreply.github.com>
This commit is contained in:
Garry Tan
2026-05-18 13:33:36 -07:00
co-authored by hnshah
parent 4192a4a365
commit 4173f9e0dc
2 changed files with 20 additions and 3 deletions
+4
View File
@@ -169,6 +169,10 @@ async function main() {
const result = JSON.parse(JSON.stringify(rawResult));
const output = formatResult(op.name, result);
if (output) process.stdout.write(output);
if (op.name === 'query') {
const { awaitPendingSearchCacheWrites } = await import('./core/search/hybrid.ts');
await awaitPendingSearchCacheWrites();
}
} catch (e: unknown) {
if (e instanceof OperationError) {
console.error(`Error [${e.code}]: ${e.message}`);
+16 -3
View File
@@ -31,6 +31,17 @@ import {
const RRF_K = 60;
const COMPILED_TRUTH_BOOST = 2.0;
const pendingCacheWrites = new Set<Promise<unknown>>();
export async function awaitPendingSearchCacheWrites(): Promise<void> {
if (pendingCacheWrites.size === 0) return;
await Promise.allSettled([...pendingCacheWrites]);
}
function trackCacheWrite(promise: Promise<unknown>): void {
pendingCacheWrites.add(promise);
promise.finally(() => pendingCacheWrites.delete(promise)).catch(() => { /* swallow */ });
}
/**
* Backlink boost coefficient. Score is multiplied by (1 + BACKLINK_BOOST_COEF * log(1 + count)).
* - 0 backlinks: factor = 1.0 (no boost).
@@ -861,9 +872,11 @@ export async function hybridSearchCached(
results.length > 0 &&
(innerMeta?.vector_enabled ?? false)
) {
void cache
.store(query, queryEmbedding, results, finalMeta, { sourceId: opts?.sourceId, knobsHash: cacheKnobsHash })
.catch(() => { /* swallow */ });
trackCacheWrite(
cache
.store(query, queryEmbedding, results, finalMeta, { sourceId: opts?.sourceId, knobsHash: cacheKnobsHash })
.catch(() => { /* swallow */ }),
);
}
return budgeted;