Skip to content

model: MN-REQ-05.5 session cap evicts least-recently-touched saveable session (proposed) - #219

Merged
chouswei merged 7 commits into
masterfrom
model/mn-req-05.5-lru-evict
Oct 20, 2026
Merged

chouswei merged 7 commits into
masterfrom
model/mn-req-05.5-lru-evict

Conversation

@chouswei

@chouswei chouswei commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

MODEL ONLY, plus tests; no engine code. The engine at 12feb2b still refuses at the cap with limit_exceeded.

MN-REQ-05.5: at the session cap (MEMNET_MAX_SESSIONS), opening or loading a session evicts the live session that has gone untouched the longest.

Rules:

  • Locking: one cap lock covers the count, the victim pick and the slot reservation. The victim is marked evicting, and its save runs outside the cap lock. The victim's own lock is held from the pick through the save, the drop and its removal from the registry.
  • Atomic save: temp file in the same directory, fsync the file, rename, fsync the directory. A crash after the fsync and before the drop is safe, because the snapshot is complete and can be restored.
  • Snapshot dir: there is no default. With no snapshot dir set, the cap refuses and nothing is evicted.
  • Unsaveable sessions: a session that can't be saved is skipped and the next-oldest is tried. Unsaveable sessions are never auto-dropped; they are counted on serve_status (sessions_unsaveable_held) for the admin.
  • Exemptions: only sessions with an open bind or live writes are exempt. The session being opened or loaded is excluded separately. There is no pin.
  • Touches: admin and serve_status reads are not touches. A load is a touch and starts a fresh TTL.
  • Restore: same ACL, no lease carried over. Reopening returns restored=true with no alerts.
  • Refusal: when nothing can be evicted, the refusal keeps limit_exceeded and says in plain words that all sessions are in use.
  • What the caller sees: callers never see the cap or eviction as a concept. Eviction is reported only to the admin and serve_status (sessions_evicted), by name or tag, never by id.
  • Behaviour model: evictSaving now leaves only on its own EvEvictSaved event.
  • No satisfy link in deploy.sysml while evictShipped=false.

Tests: tests/test_session_evict.py has 22 runtime pytest checks on the in-process engine, using MEMNET_MAX_SESSIONS=3 and a temp snapshot dir. They cover eviction order, exemptions, restore fidelity (rows, edges, ACL, no lease), refusal, no snapshot dir, cap=2 concurrent opens, the atomic write and crash safety, and touches. The whole module is marked xfail (non-strict) until the engine ships, so CI stays green. Make the mark strict when the engine lands. MN-VER-05-S03 to S09 now point to these tests.

Later, not dropped:

  • Idle window: a 'touched in the last N minutes' exemption waits until a run measures reload-thrash at the cap. Future test: at cap=3, cycle opens over 4 or more live sessions and count evict-then-reload within a short interval.
  • Evicted-id refusal: a distinct session_evicted refusal for a caller holding an evicted id.
  • Same id: whether a load returns the same id.

Built by Claude haiku, reviewed by Claude sonnet, review fixes applied. Do not merge without owner review.

Copy link
Copy Markdown
Owner Author

ba87659 (re-review of 38ba83a)

Blocking items:

  • R1: added a positive control. With the snapshot dir set, a full cap evicts. With it unset, the open is refused and every session stays live.
  • R2: the fsync test checks the file type of each fsynced handle. It requires a regular-file fsync, then the rename, then a directory fsync, compared by realpath.

Also done:

  • R3: MCP and CLI open/load results carry no cap, limit, count or evict fields. The requirement notes that limit_exceeded itself reveals a cap.
  • R4: the reserve lease is present before eviction and absent after load, and the lost lease then fails cleanly with reserve_expired. No rows change.
  • R5: the doc says a fresh open can't be its own victim.

Decisions:

  • (a) eviction reuses MEMNET_EXPIRE_SNAPSHOT_DIR; MEMNET_EVICT_SNAPSHOT_DIR is gone. MEMNET_SNAPSHOT_DIR is allowed as an alias, but start is refused with config_conflict if the two point to different paths. The error never prints either path.
  • (b) a victim keeps its slot until it's dropped, and the new open waits for the save. The cap=2 test with 4 concurrent opens requires exactly 2 opened and 2 refused, and the count stays at the cap during the save.
  • (c) the ship-time single commit is documented: strict=True, evictShipped=true, the satisfy link and the S03 to S09 guards together. test_ship_flags_flip_together enforces it now and is not xfail.

Tests: 128 passed and 26 xfailed (-k "sysml or evict or session"). ruff is clean.

…check note; ship doc; operator doc + changelog
…r lost-lease claim; modified_at touch field; reserved on other-llm rid
…hip-sync strict xfails, surface split, classifier, helper constants
@chouswei
chouswei marked this pull request as ready for review October 20, 2026 22:23
@chouswei
chouswei merged commit b2636ab into master Oct 20, 2026
2 checks passed
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