Skip to content

fix: speed up fluree-memory adds and stop them hanging under concurrency#1384

Open
bplatz wants to merge 2 commits into
mainfrom
fix/mcp-memory-append-union
Open

fix: speed up fluree-memory adds and stop them hanging under concurrency#1384
bplatz wants to merge 2 commits into
mainfrom
fix/mcp-memory-append-union

Conversation

@bplatz

@bplatz bplatz commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Problem

memory_add (CLI and MCP) re-read every memory from the __memory ledger via a SPARQL query (all_memories_for_scope) and rewrote the whole repo.ttl on each call. Under concurrent MCP requests — e.g. an agent saving 2–3 memories at once, which the client dispatches in parallel — that O(N) work serialized behind the per-process mutation lock and made adds take seconds (~13.5s at 100 memories), which users experienced as hangs. A separately-observed failure mode: a wedged operation could hold a lock forever (a CPU-spun MCP server froze every later call in that process).

Changes

  • Fast add via sorted file splice. add now inserts the new block at its sorted (branch, id) position by reading only the .ttl text (turtle_io::insert_memory_into_file), never the ledger. Untouched blocks stay byte-identical (asserted equal to a full rewrite), so the on-disk result and merge behavior are unchanged. The add itself drops to ~2ms; the per-add ledger round-trip is gone.
  • Merge behavior preserved and safe. The file keeps its (branch, id) sort under a default git merge: distinct-branch adds auto-merge, while two branches changing the same memory correctly conflict — no silent corruption.
  • Bounded locks. The cross-process flock now polls try_lock to a deadline, and the in-process mutation lock uses a timeout, so a wedged operation surfaces a recoverable error instead of freezing the session. Shared file_sync::lock_timeout (30s default, override with FLUREE_MEMORY_LOCK_TIMEOUT_SECS).
  • Memory docs updated to match.

Tests

  • it_merge: distinct-region adds merge cleanly; same-memory edits conflict.
  • turtle_io: incremental splice equals a full sorted rewrite byte-for-byte.
  • file_sync: the rebuild lock times out when already held.

@bplatz bplatz requested review from aaj3f and zonotope June 26, 2026 23:13
bplatz added 2 commits June 26, 2026 20:31
memory_add re-read every memory from the ledger (all_memories_for_scope,
a SPARQL round-trip) and rewrote the whole file on each call. Under
concurrent MCP requests that O(N) work serialized behind the mutation
lock and made adds take seconds (13.5s at N=100), which read as hangs.
A wedged op could also hold a lock forever (an observed CPU-spun server
froze every later call).

- add() now splices the new block into its sorted (branch, id) position
  by reading only the .ttl text (turtle_io::insert_memory_into_file) —
  never the ledger. Untouched blocks stay byte-identical, so the file is
  identical to a full rewrite (asserted) and concurrent adds on different
  branches still merge cleanly under a *default* git merge, while edits
  to the same memory correctly conflict. No merge=union, so update/forget
  can't silently merge-corrupt.
- both locks are now bounded: the cross-process flock polls try_lock to a
  deadline, and the in-process mutation lock uses a timeout (lock_mutation);
  shared file_sync::lock_timeout, 30s default, FLUREE_MEMORY_LOCK_TIMEOUT_SECS.
- tests: splice == full-rewrite byte-for-byte; distinct-region adds merge
  clean; same-memory edits conflict; flock times out when held.
The add path now splices a memory block into its sorted (branch, id)
position by reading the .ttl text rather than re-deriving the file from
the ledger; update/forget still rewrite. Note the bounded lock wait, and
make explicit that a same-memory edit on two branches conflicts by design
(default git merge) rather than silently merging.
@bplatz bplatz force-pushed the fix/mcp-memory-append-union branch from 5d0a7da to e2a579e Compare June 27, 2026 00:32
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