Skip to content

Bug: graph-reset leaves pre-reset rows queryable and races graph-extract #1189

Open

Description

@Tomauskasz

User problem

After POST /agentmemory/graph/reset reports success, Graph queries can still return nodes and relationships from before the reset. A concurrent mem::graph-extract can also overwrite the reset snapshot with a snapshot derived from the previous Graph state.

This is a correctness problem on small Graphs. It is independent of the large-corpus worker failure reported in #825.

Environment

  • Published agentmemory: @agentmemory/agentmemory@0.9.28
  • Upstream main: package version 0.9.29, commit 2973e4e
  • Node: v26.4.0
  • OS: macOS 26.6.1 arm64

Reproduction

  1. Create one Graph node and one relationship through normal extraction.
  2. Confirm that POST /agentmemory/graph/query with a matching query or the node's startNodeId returns them.
  3. Call POST /agentmemory/graph/reset.
  4. Repeat the same query or startNodeId request.
  5. For the race case, use a controlled provider or test double to pause mem::graph-extract after it reads the current Graph snapshot. Run mem::graph-reset, then allow extraction to continue.

Actual result

mem::graph-reset writes only an empty KV.graphSnapshot with resetAt. The source explicitly leaves KV.graphNodes and KV.graphEdges on disk as orphans:

The query and startNodeId paths enumerate the raw node and relationship scopes and filter only on stale. They do not exclude records older than snapshot.resetAt:

GraphRetrieval.searchByEntities, expandFromChunks, and temporalQuery have the same behavior:

The extraction persist path reads the snapshot, writes nodes, relationships, lookup indexes, and degrees, and writes the snapshot at the end. Reset does not share one operation-wide serialization boundary with that sequence:

This permits the following ordering:

  1. Extraction reads the pre-reset snapshot.
  2. Reset writes the empty snapshot and returns success.
  3. Extraction writes its nodes and relationships.
  4. Extraction writes its pre-reset-derived snapshot after the reset.

Expected result

A successful reset must establish one clear Graph generation boundary:

  • No Graph read returns pre-reset nodes or relationships.
  • All Graph read paths apply the same generation rule.
  • Reset and extraction are ordered atomically.
  • An extraction completed before reset is hidden or removed by reset.
  • An extraction committed after reset writes only into the new generation.
  • Reset does not report success while an in-flight extraction can restore the old snapshot.

The implementation can use physical deletion, generation-aware filtering, or another design. The required result is consistent isolation across every Graph read and write path.

Related work

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions