Skip to content

Performance: Unbounded maps and memory leaks across modules #357

@OneStepAt4time

Description

@OneStepAt4time

Summary

Several maps and buffers grow without bound across the codebase, causing memory leaks under sustained use.

Details

1. IP rate limits map (server.ts:130-146)

ipRateLimits (Map<string, number[]>) is never pruned. Every unique IP adds an entry that persists forever. timestamps.shift() on each check is O(n) for that IP's array.

2. Per-session event buffers (events.ts:68-69, 109-118)

eventBuffers stores up to 50 events per session but entries are never removed when a session ends. Only bulk-cleared in destroy(). The emitEnded method deletes the emitter but not the buffer.

3. Per-session metrics (metrics.ts:72-73)

perSession map grows indefinitely. sessionCompleted and sessionFailed increment counters but never remove the session entry. statusChanges array inside each entry also grows without bound.

4. Full JSONL reparse on every read (session.ts:976, 1023)

readTranscript() and getSummary() always parse the entire JSONL file from offset 0. For long-running sessions with multi-MB transcripts, this is O(n) per API call.

5. save() on every API read (session.ts:901)

readMessages() persists the full session state to disk on every GET request. Under load, this creates excessive disk I/O.

6. Excessive fixed sleeps in tmux (tmux.ts:325, 416, 505)

Creating a session takes ~8+ seconds in fixed sleeps alone (2000ms + 500ms + 1000-2000ms + up to 4800ms in retry delays). Should use polling instead of fixed delays.

7. windowExists overhead (tmux.ts:482-486)

Every sendKeys calls windowExistslistWindows → 3 tmux CLI invocations. With 3 retry attempts, that's up to 27 CLI calls just for existence checks per message.

8. activeSubagents array (session.ts:471-485)

Uses Array.includes() (O(n)) for dedup instead of Set.has() (O(1)). Persisted to state.json, growing the file.

9. SSE token count not decremented on consume (auth.ts:207-227)

validateSSEToken deletes consumed tokens but doesn't decrement sseTokenCounts, leaving counts artificially high until next cleanup cycle.

Suggested Fix

  1. Add periodic cleanup for ipRateLimits (prune IPs with all timestamps older than window)
  2. Delete eventBuffers entry alongside emitters in emitEnded
  3. Add cleanupSession(sessionId) to MetricsCollector, call on session destroy
  4. Cache parsed JSONL entries; only read delta from byteOffset
  5. Debounce save for offset-only changes
  6. Replace fixed sleeps with polling loops in tmux
  7. Cache window existence with short TTL
  8. Use Set<string> for activeSubagents
  9. Decrement sseTokenCounts in validateSSEToken

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions