Skip to content

fix(memory): bound in-memory per-session tracking against unclean session ends#64

Merged
alexgreensh merged 1 commit into
alexgreensh:mainfrom
danikdanik:fix/bounded-session-maps
Jun 16, 2026
Merged

fix(memory): bound in-memory per-session tracking against unclean session ends#64
alexgreensh merged 1 commit into
alexgreensh:mainfrom
danikdanik:fix/bounded-session-maps

Conversation

@danikdanik

Copy link
Copy Markdown
Contributor

Why

Three continuity/fresh-nudge collections in index.ts (_continuityInjectedSessions, _freshNudgeFiredSessions, _freshNudgePriorScores) and the states Map in checkpoint-policy.ts are pruned on session:end. A session that ends uncleanly (process killed, network disconnect) never fires that event, so its entries leak for the gateway's entire lifetime. On an always-on gateway these grow without bound.

What

Add oldest-first eviction caps (MAX_TRACKED_SESSIONS = 2000):

  • index.ts gets boundedSetAdd() / boundedMapSet() helpers. The Map variant refreshes recency on update (_freshNudgePriorScores is re-set every patch, so this keeps an actively-scored session from being evicted). The Set guards are add-once fire-guards, so they intentionally do not refresh — a refresh would be dead code.
  • checkpoint-policy.ts gets rememberState().
  • The session:end cleanup is unchanged and remains the normal release path; the cap is only a backstop for sessions that never get one.

Evicting a checkpoint state is safe: the dedup-critical fields (capturedFillBands, capturedQualityThresholds, capturedMilestones, lastCheckpointAt) are written through to disk on every recordCheckpointDecision(), and getCheckpointState() re-hydrates an evicted entry from disk — so a band/threshold/milestone is never re-captured. The transient counters (editWriteCount, editedFiles, lastEvaluatedAt) are not persisted; evicting an uncheckpointed session resets them, which only delays a milestone-edit-batch checkpoint (conservative, never spurious). These trade-offs are documented in the code.

How (verified)

Verified the eviction algorithm: inserting 2500 sessions caps size at 2000, evicts the oldest, retains the newest, and a refreshed key survives a later eviction wave. tsc build is green; committed dist/ rebuilt.

Deliberately not addressed

The cap is hardcoded (not a new TOKEN_OPTIMIZER_* env knob) and the eviction helper is not extracted to a shared module — both kept out to keep this a minimal, focused backstop rather than an API/structure change.

…sion ends

Why: three continuity/fresh-nudge collections in index.ts
(_continuityInjectedSessions, _freshNudgeFiredSessions, _freshNudgePriorScores)
and the states Map in checkpoint-policy.ts are pruned on session:end. A session
that ends uncleanly (process killed, network disconnect) never fires that event,
so its entries leak for the gateway's entire lifetime. On an always-on gateway
these grow without bound.

What: add oldest-first eviction caps (MAX_TRACKED_SESSIONS = 2000). index.ts
gets boundedSetAdd()/boundedMapSet() helpers (the Map variant refreshes recency
on update so an actively-scored session is not evicted mid-use); checkpoint-
policy.ts gets rememberState(). Evicting a checkpoint state is safe -- every
mutation is written through to disk by persistState(), and getCheckpointState()
re-hydrates an evicted entry from disk on the next access. The session:end
cleanup is unchanged and remains the normal release path; the cap is only a
backstop for sessions that never get one.

How: verified the eviction algorithm -- inserting 2500 sessions caps size at
2000, evicts the oldest, retains the newest, and a refreshed key survives a
later eviction wave. tsc build green; dist rebuilt.
@alexgreensh alexgreensh merged commit 9a17189 into alexgreensh:main Jun 16, 2026
1 check passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 16, 2026
@alexgreensh

Copy link
Copy Markdown
Owner

Merged in v5.11.13 — good catch on the map growth when sessions end uncleanly. Thanks, Dani.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants