From 6fa375c9fd876d273635f8fe17faf4250144ed41 Mon Sep 17 00:00:00 2001 From: unohee Date: Thu, 16 Jul 2026 11:31:24 +0900 Subject: [PATCH] fix(memory): drop per-search lastAccessed write that caused Lance concurrent-writer errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit searchMemorySafe fired a Lance table.update() on every retrieval to stamp lastAccessed. Under 'openswarm review --max' (up to 16 concurrent reviewer subagents each recalling memory) those commits collided on Lance's optimistic- concurrency retry limit → 'Too many concurrent writers … [Memory] Failed to update access time'. lastAccessed has no read consumer: retrieval recency uses createdAt (access-frequency/decay was already removed) and no CLI surfaces it, so the write was pure contention with zero benefit. Remove it (and the now-unused updateAccessTime helper). The schema field stays, set at record creation, so a batched/serialized decay writer can be reintroduced later if needed. --- src/memory/memoryCore.ts | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/memory/memoryCore.ts b/src/memory/memoryCore.ts index 2401dabc..ac51255e 100644 --- a/src/memory/memoryCore.ts +++ b/src/memory/memoryCore.ts @@ -903,7 +903,16 @@ export async function searchMemorySafe( .sort((a, b) => b.hybridScore - a.hybridScore) .slice(0, limit); - updateAccessTime(scored.map(s => s.record.id)).catch((e) => console.warn('[Memory] Failed to update access time:', e)); + // NOTE: we deliberately do NOT write a `lastAccessed` timestamp back here. + // Every search used to fire a Lance `table.update()` commit, but `lastAccessed` + // feeds nothing — retrieval recency uses `createdAt` (access-frequency/decay was + // removed, see calculateFreshness below), and no CLI reads the field. Under + // `openswarm review --max` (up to 16 concurrent reviewer subagents each searching + // memory) those per-search commits collided on Lance's optimistic-concurrency + // limit → "Too many concurrent writers … [Memory] Failed to update access time". + // Dropping a write with no read consumer removes the contention entirely. The + // schema field remains (set at record creation) so decay can be reintroduced with + // a batched/serialized writer if ever needed. const formatted: MemorySearchResult[] = scored.map(({ record: r, similarity, recency, importance, hybridScore }) => ({ id: r.id, @@ -952,16 +961,3 @@ export async function searchMemory( return result.memories; } -/** - * Update last_accessed timestamp for retrieved memories - */ -async function updateAccessTime(ids: string[]): Promise { - if (!table || ids.length === 0) return; - - const uniqueIds = [...new Set(ids)]; - const quotedIds = uniqueIds.map(id => `'${String(id).replace(/'/g, "''")}'`).join(', '); - await table.update({ - where: `id IN (${quotedIds})`, - values: { lastAccessed: Date.now() }, - }); -}