Skip to content

fix(memory): retry Lance writes on concurrency conflict — store path (INT-2817)#304

Merged
unohee merged 1 commit into
mainfrom
fix/memory-write-retry-concurrency
Jul 16, 2026
Merged

fix(memory): retry Lance writes on concurrency conflict — store path (INT-2817)#304
unohee merged 1 commit into
mainfrom
fix/memory-write-retry-concurrency

Conversation

@unohee

@unohee unohee commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Follow-up to #303. That PR removed the per-search lastAccessed write; this hardens the remaining store path the residual note called out.

Problem

Under openswarm review --max, up to 16 reviewer subagents run as separate processes sharing one on-disk Lance table. table.add (and predicated update/delete) commit optimistically, and Lance's built-in retry budget (~2 attempts / 30s) is too small for that fan-out → Too many concurrent writers. An in-process mutex can't serialize across processes.

Fix

withMemoryWriteRetry(op, label) — retry on optimistic-concurrency conflicts with exponential backoff + full jitter (max 8 attempts) so racing writers desynchronize and re-commit against the latest table version. Appends and predicated update/delete are safe to re-run (Lance re-commits against latest each attempt, so no double-apply). Non-retryable errors rethrow immediately; the matcher is kept tight to genuine conflict signals (concurrent writers / commit conflict / version conflict / retry_timeout).

Applied to all 5 memory write sites:

  • memoryCore.ts: saveMemory add, saveCognitiveMemory add, deleteMemoriesByDerivedFrom delete
  • memoryOps.ts: updateMemoryRecord update, deleteMemoryIds delete

Verification

  • npx tsc --noEmit — clean
  • src/memory/ — 36 pass, incl. new memoryWriteRetry.test.ts (success / retry-then-succeed / non-retryable rethrow / give-up-after-cap, via fake timers)
  • openswarm review — APPROVE (suggested tightening the retryable matcher → applied; too many dropped as redundant/over-broad)

Deferred

Reviewer suggested a cross-process concurrent-write stress harness — genuinely useful but heavier/flakier than this fix warrants; left as a future item.

🤖 Generated with Claude Code

…INT-2817)

The lastAccessed fix (#303) removed the per-search write, but the store path
(table.add) and predicated update/delete can still collide under
'openswarm review --max' — up to 16 reviewer subagents are SEPARATE processes
sharing one on-disk Lance table, and Lance's built-in commit-retry budget
(~2 attempts/30s) is too small for that fan-out. An in-process mutex can't help
across processes, so wrap every memory write in withMemoryWriteRetry: retry on
optimistic-concurrency conflicts with exponential backoff + full jitter (max 8
attempts) so racing writers desynchronize and re-commit against the latest
version. Applied to all 5 write sites (add x2, delete x2, update x1). Non-
retryable errors rethrow immediately; matcher kept tight to real conflict
signals.
@unohee
unohee merged commit 419a0fe into main Jul 16, 2026
5 checks passed
@unohee
unohee deleted the fix/memory-write-retry-concurrency branch July 16, 2026 02:49
@unohee unohee mentioned this pull request Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant