Skip to content

Fix generateLargeGraphStore() building edges over detached nodes - #292

Merged
mbastian merged 3 commits into
masterfrom
fix/multiblock-garbage-round-trip
Aug 25, 2026
Merged

Fix generateLargeGraphStore() building edges over detached nodes#292
mbastian merged 3 commits into
masterfrom
fix/multiblock-garbage-round-trip

Conversation

@mbastian

Copy link
Copy Markdown
Member

Summary

Root-caused the "pre-existing bug" flagged in #291: it's not a Serialization/GraphStore correctness issue, it's a test-helper construction flaw.

GraphGenerator.generateLargeGraphStore() built its node list via generateLargeNodeList() and its edge list via generateLargeEdgeList() independently. The latter internally creates its own, differently-sized, throwaway NodeStore to source edge endpoints from — so edge.source/edge.target never actually pointed at the nodes inserted into the real GraphStore. They only lined up numerically (same storeId value) by coincidence of both being simple sequential-counter allocations, which is why everything looked fine as long as no node was ever removed afterward.

Confirmed directly: graphStore.nodeStore.get(edge.source.storeId) != edge.source for every edge, before this fix.

Once a referenced node is removed, GraphStore.removeNode()'s cascade (which walks the real node's own in/out adjacency links to find and remove incident edges) finds nothing to remove — because those links were never wired to the edges that "coincidentally" reference that storeId — leaving a dangling edge behind. That only became visible once something both spanned multiple storage blocks and had elements removed afterward, which is exactly what came up while testing #291.

Fix: build edges via generateEdgeList(graphStore.nodeStore, ...) so they reference the real, inserted nodes. generateLargeNodeList()/generateLargeEdgeList() are untouched (other tests use them independently, as standalone lists, not paired) — and generateLargeGraphStore() itself was unused anywhere in the suite before this session's new tests, so nothing depended on the old behavior.

Test plan

  • New regression test: generateLargeGraphStore() + scattered node/edge removal (multi-block, matches the scenario that surfaced this) + full serialize/deserialize round-trip, asserting deepEquals. Fails red on the old generator, passes green with the fix.
  • Full suite green (1641 tests), run 2x.

🤖 Generated with Claude Code

…ched nodes

generateLargeEdgeList() creates edges against its own independently-
sized, throwaway NodeStore rather than the real one - so edge.source/
target only lined up with generateLargeGraphStore()'s actual inserted
nodes by numeric coincidence (matching storeId, different objects).
removeNode()'s cascade-edge-removal walks the real node's own
adjacency links, which were never wired to these edges, so removing a
referenced node silently left a dangling edge behind instead of
cascading. That only surfaced once something both spanned multiple
storage blocks and had elements removed afterward - traced back to
plain, pre-existing sequential serialization code, not anything
specific to threading.

Root cause confirmed directly: graphStore.nodeStore.get(edge.source.
storeId) != edge.source for every edge before this fix.

Fix: build edges via generateEdgeList(graphStore.nodeStore, ...) so
they reference the real nodes. generateLargeNodeList()/
generateLargeEdgeList() are left untouched since other tests use them
independently; generateLargeGraphStore() was unused before this
session's tests, so nothing depended on the old behavior.
The fix touches GraphGenerator, not Serialization, so the test should
exercise that directly rather than proving it indirectly through a
full serialize/deserialize round-trip. Asserts the actual invariant
that broke: every edge's source/target is the same object registered
in the store's own nodeStore, not just one with a matching storeId.
Confirmed red on the pre-fix generator, green after.
@mbastian
mbastian merged commit 4fa8129 into master Aug 25, 2026
1 check passed
@mbastian mbastian added this to the 0.8.7 milestone Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant