Skip to content

Commit 0ede3d7

Browse files
fix(memory-core): yield event loop during fallback vector search (#81172)
1 parent 9c476b4 commit 0ede3d7

1 file changed

Lines changed: 18 additions & 15 deletions

File tree

extensions/memory-core/src/memory/manager-search.test.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ describe("searchKeyword FTS MATCH fallback", () => {
254254
snippetMaxChars: 200,
255255
sourceFilter: { sql: "", params: [] },
256256
buildFtsQuery: brokenBuildFtsQuery,
257-
bm25RankToScore: bm25RankToScore,
257+
bm25RankToScore,
258258
});
259259

260260
// LIKE fallback should find "Agent" in the first row
@@ -377,7 +377,7 @@ describe("searchKeyword FTS MATCH fallback", () => {
377377
snippetMaxChars: 200,
378378
sourceFilter: { sql: "", params: [] },
379379
buildFtsQuery: brokenBuildFtsQuery,
380-
bm25RankToScore: bm25RankToScore,
380+
bm25RankToScore,
381381
});
382382

383383
// Per-token fallback: both "Agent" AND "cron" must match
@@ -407,7 +407,7 @@ describe("searchKeyword FTS MATCH fallback", () => {
407407
snippetMaxChars: 200,
408408
sourceFilter: { sql: "", params: [] },
409409
buildFtsQuery: () => "BROKEN <<<",
410-
bm25RankToScore: bm25RankToScore,
410+
bm25RankToScore,
411411
});
412412

413413
expect(warnSpy).toHaveBeenCalledTimes(1);
@@ -487,8 +487,9 @@ describe("searchVector sqlite-vec KNN", () => {
487487
// Real Nextcloud-scale corpus where the vec0 fast path is unavailable
488488
// (e.g., extension not loaded or dimension mismatch with active model)
489489
// used to pin the main thread for the entire fallback scan, blocking
490-
// channel I/O. After fix the loop yields every FALLBACK_VECTOR_YIELD_EVERY
491-
// rows so a setImmediate-scheduled task can interleave between batches.
490+
// channel I/O. After fix the loop yields after each full
491+
// FALLBACK_VECTOR_BATCH_SIZE batch so a setImmediate-scheduled task can
492+
// interleave between batches.
492493
const db = new DatabaseSync(":memory:");
493494
try {
494495
ensureMemoryIndexSchema({
@@ -502,7 +503,7 @@ describe("searchVector sqlite-vec KNN", () => {
502503
const insertChunk = db.prepare(
503504
"INSERT INTO chunks (id, path, source, start_line, end_line, hash, model, text, embedding, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
504505
);
505-
// Just over the yield batch (FALLBACK_VECTOR_YIELD_EVERY=256) so we
506+
// Just over 3x the yield batch (FALLBACK_VECTOR_BATCH_SIZE=256), so we
506507
// expect at least 3 yield points to fire during the scan.
507508
const N = 1024;
508509
for (let i = 0; i < N; i += 1) {
@@ -593,7 +594,7 @@ describe("searchVector sqlite-vec KNN", () => {
593594
it("returns an empty result set when no chunks match the provider model", async () => {
594595
const db = createFallbackDb();
595596
try {
596-
// One chunk with a different model must not appear in results.
597+
// One chunk with a different model must not appear in results.
597598
insertFallbackChunk(db, { id: "other-only", model: "other-model", vector: [1, 0] });
598599
const results = await searchVector({
599600
db,
@@ -636,7 +637,7 @@ describe("searchVector sqlite-vec KNN", () => {
636637
it("handles an exact batch-size boundary (FALLBACK_VECTOR_BATCH_SIZE rows)", async () => {
637638
// When N === FALLBACK_VECTOR_BATCH_SIZE exactly, the loop produces one
638639
// full batch and then must take one extra empty-batch step before
639-
// breaking verify no row is dropped or double-counted at the seam.
640+
// breaking; verify no row is dropped or double-counted at the seam.
640641
const db = createFallbackDb();
641642
try {
642643
const N = 256;
@@ -660,7 +661,7 @@ describe("searchVector sqlite-vec KNN", () => {
660661
sourceFilterChunks: { sql: "", params: [] },
661662
});
662663
expect(results).toHaveLength(3);
663-
// Strictly decreasing scores confirms top-K maintenance is intact.
664+
// Strictly decreasing scores confirms top-K maintenance is intact.
664665
for (let i = 1; i < results.length; i += 1) {
665666
expect(results[i - 1].score).toBeGreaterThan(results[i].score);
666667
}
@@ -672,7 +673,7 @@ describe("searchVector sqlite-vec KNN", () => {
672673
it("preserves top-K ordering vs. a naive reference cosine implementation", async () => {
673674
// Guards against accidental algorithmic regressions from the control-flow
674675
// refactor: insert 200 chunks with random vectors and assert our patched
675-
// fallback search returns the same top-K (by id, in the same order) as a
676+
// fallback search returns the same top-K by id, in the same order, as a
676677
// straight-line JS reference that scores every row.
677678
const db = createFallbackDb();
678679
try {
@@ -707,7 +708,7 @@ describe("searchVector sqlite-vec KNN", () => {
707708
}
708709
const referenceTopIds = chunks
709710
.map((c) => ({ id: c.id, score: refCosine(queryVec, c.vector) }))
710-
.sort((a, b) => b.score - a.score)
711+
.toSorted((a, b) => b.score - a.score)
711712
.slice(0, limit)
712713
.map((r) => r.id);
713714

@@ -731,17 +732,17 @@ describe("searchVector sqlite-vec KNN", () => {
731732
it("picks up rows inserted during the inter-batch event-loop yield (rowid cursor)", async () => {
732733
// The fix's rowid-paginated batches yield via setImmediate between batches.
733734
// Schedule an INSERT to land in that yield gap and verify the search picks
734-
// up the new rows in the next batch no double-counting, no missed rows.
735+
// up the new rows in the next batch: no double-counting, no missed rows.
735736
const db = createFallbackDb();
736737
try {
737738
// 257 baseline rows: first batch sees 256 (score 0 vs. query), second
738-
// batch would have seen just 1 until our setImmediate insert lands.
739+
// batch would have seen just 1 until our setImmediate insert lands.
739740
const baselineCount = 257;
740741
for (let i = 0; i < baselineCount; i += 1) {
741742
insertFallbackChunk(db, {
742743
id: `baseline-${i}`,
743744
model: "target-model",
744-
// perpendicular to the querycosine 0
745+
// Perpendicular to the query: cosine 0.
745746
vector: [0, 1],
746747
});
747748
}
@@ -752,7 +753,9 @@ describe("searchVector sqlite-vec KNN", () => {
752753
// must include them in batch 2.
753754
let inserted = false;
754755
const insertDuringYield = (): void => {
755-
if (inserted) return;
756+
if (inserted) {
757+
return;
758+
}
756759
inserted = true;
757760
insertFallbackChunk(db, {
758761
id: "winner-A",

0 commit comments

Comments
 (0)