Skip to content

Isolate serve I/O, fix mutate and snapshot honesty, and stop false orphan prunes - #206

Merged
chouswei merged 1 commit into
masterfrom
cursor/serve-isolation-mutate-snapshot-14d1
Oct 8, 2026
Merged

chouswei merged 1 commit into
masterfrom
cursor/serve-isolation-mutate-snapshot-14d1

Conversation

@chouswei

@chouswei chouswei commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Summary

Endleaf Keeper reports against 0.19.18 and 0.19.20, plus a housekeep data-loss hazard. No version bump and no release.

  • MN-REQ-06.13 Concurrent memnet serve commands no longer share stdout/stderr. Each request captures streams on contextvars. TCP and memnet-mcp --transport gateway are covered. No serve-wide lock. Listen backlog is 128.
  • Mutate stdin, leftover pipe, and GQL statement breaks split on LF only (optional trailing CR). Other splitlines separators stay inside the value.
  • Snapshot save widens extras even when the CREATE label case differs from the SCHEMA tag, and appends id when the live SCHEMA omitted it. An unloadable key fails closed.
  • Labelled MATCH (n:Label) SET case-folds the label. MATCH … MATCH … CREATE stays parse_error. TTL is minutes; expiry is checked on access.
  • MN-REQ-04.12 Housekeep resolves edge endpoints by hidden element id and nickname. prune orphans / prune stale --apply refuse prune_referenced instead of deleting a node an edge still names.
  • Product gateway contract matches the deployed 0.19.20 pin (pi-endleaf, MEMNET_GATEWAY_TOKEN, HTTP status for every gateway code, decision 11 closed, concurrency).

Root cause (serve output mix-up)

_handle_request and in-process run_argv assigned sys.stdout, sys.stderr, and sys.stdin to one shared pair of buffers for the duration of the command. The TCP server is a ThreadingTCPServer, so two commands in flight wrote into whichever buffer was installed last. Exit codes are locals and did not themselves swap; the success text (ok=1 fail=0 and the other session's records) did, so a failed write could look successful.

Throughput

No serve-wide lock. On this VM, 12 successful TCP mutates at once took 0.494 s against 0.525 s one after another (ratio 0.94). The work is CPU-bound under the GIL, so the overlap is small. The added cost is a contextvar lookup per write, not a queue.

Answers

  • Line breaks. Statements split on LF only. VT, FF, FS, GS, RS, NEL, LS, and PS are kept literally inside the value (same choice as snapshot escaping). An embedded CR is not a break.
  • Undeclared properties. One undeclared property failed save because extras were keyed by the stored label spelling (Cst) while the SCHEMA tag is CST, so the extra never reached the emitted map. Fixed by canonical tag. A SCHEMA without id is still legal at open (id need not be first or present). Save appends id on the snapshot SCHEMA and mints a nickname. The live map is unchanged until load. A key that cannot reload (foo bar) is snapshot_unsaveable.
  • Labelled MATCH. Intended to match. Mixed-case labels missed or raised id_conflict because the stored tag was the raw spelling. Labels now case-fold. A different known label that matches nothing is not_found.
  • MATCH-MATCH edge create. Intended refusal: parse_error. The comma form with --allow-new-relation creates the edge.
  • TTL 60. Intended. --ttl is minutes (1..1440). 130 s idle is inside 60 minutes. There is no background sweeper. purge_expired runs on access, and a successful access slides the deadline. Access after real expiry refuses session_expired.
  • Housekeep. GQL edges store _elN. The orphan check compared each node's nickname to those ids, so every node looked unreferenced and prune orphans --apply would delete them. Nothing automatic runs prune --apply (no sweep, no MCP tool). Only the CLI does, and only with --apply.

Test plan

  • pytest (full suite, one live AgensGraph test skipped)
  • ruff check and ruff format --check on the CI paths
  • CI on this PR
Open in Web Open in Cursor 

Concurrent serve commands captured stdout on process-global streams, so
envelopes could swap. Each request now uses context-local streams.
Mutate splits on LF only, snapshot save keeps undeclared properties and
a missing id column, and housekeep resolves edge ends by hid and nickname
before prune --apply will delete anything.

Co-authored-by: chouswei <chouswei@users.noreply.github.com>
@chouswei
chouswei marked this pull request as ready for review October 8, 2026 17:40
@chouswei
chouswei merged commit e9d11f5 into master Oct 8, 2026
2 checks passed
chouswei added a commit that referenced this pull request Oct 8, 2026
Hatch, project.toml, changelog, and the version map name the serve request isolation and housekeep endpoint fixes (#206). Not 0.20.
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.

2 participants