From 3f5351529f778fb997fda8d6169be97f61079dd8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 14 Feb 2026 19:34:04 +0000 Subject: [PATCH] perf(test): skip atomic sqlite swaps for memory index --- src/memory/index.test.ts | 3 ++ src/memory/manager-sync-ops.ts | 66 +++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/memory/index.test.ts b/src/memory/index.test.ts index b82f06afa0b..f0cca4781d1 100644 --- a/src/memory/index.test.ts +++ b/src/memory/index.test.ts @@ -57,6 +57,9 @@ describe("memory index", () => { }); beforeEach(async () => { + // Perf: most suites don't need atomic swap behavior for full reindexes. + // Keep atomic reindex tests on the safe path. + vi.stubEnv("OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX", "1"); embedBatchCalls = 0; workspaceDir = path.join(fixtureRoot, `case-${fixtureCount++}`); await fs.mkdir(workspaceDir, { recursive: true }); diff --git a/src/memory/manager-sync-ops.ts b/src/memory/manager-sync-ops.ts index fe553ef40fe..e04deeefac5 100644 --- a/src/memory/manager-sync-ops.ts +++ b/src/memory/manager-sync-ops.ts @@ -744,11 +744,22 @@ class MemoryManagerSyncOps { (vectorReady && !meta?.vectorDims); try { if (needsFullReindex) { - await this.runSafeReindex({ - reason: params?.reason, - force: params?.force, - progress: progress ?? undefined, - }); + if ( + process.env.OPENCLAW_TEST_FAST === "1" && + process.env.OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX === "1" + ) { + await this.runUnsafeReindex({ + reason: params?.reason, + force: params?.force, + progress: progress ?? undefined, + }); + } else { + await this.runSafeReindex({ + reason: params?.reason, + force: params?.force, + progress: progress ?? undefined, + }); + } return; } @@ -958,6 +969,51 @@ class MemoryManagerSyncOps { } } + private async runUnsafeReindex(params: { + reason?: string; + force?: boolean; + progress?: MemorySyncProgressState; + }): Promise { + // Perf: for test runs, skip atomic temp-db swapping. The index is isolated + // under the per-test HOME anyway, and this cuts substantial fs+sqlite churn. + this.resetIndex(); + + const shouldSyncMemory = this.sources.has("memory"); + const shouldSyncSessions = this.shouldSyncSessions( + { reason: params.reason, force: params.force }, + true, + ); + + if (shouldSyncMemory) { + await this.syncMemoryFiles({ needsFullReindex: true, progress: params.progress }); + this.dirty = false; + } + + if (shouldSyncSessions) { + await this.syncSessionFiles({ needsFullReindex: true, progress: params.progress }); + this.sessionsDirty = false; + this.sessionsDirtyFiles.clear(); + } else if (this.sessionsDirtyFiles.size > 0) { + this.sessionsDirty = true; + } else { + this.sessionsDirty = false; + } + + const nextMeta: MemoryIndexMeta = { + model: this.provider.model, + provider: this.provider.id, + providerKey: this.providerKey, + chunkTokens: this.settings.chunking.tokens, + chunkOverlap: this.settings.chunking.overlap, + }; + if (this.vector.available && this.vector.dims) { + nextMeta.vectorDims = this.vector.dims; + } + + this.writeMeta(nextMeta); + this.pruneEmbeddingCacheIfNeeded(); + } + private resetIndex() { this.db.exec(`DELETE FROM files`); this.db.exec(`DELETE FROM chunks`);