Skip to content

JSONL session caching: eliminate redundant file reads and N+1 HTTP requests #221

@mattleaverton

Description

@mattleaverton

Problem

When loading session history (timeline pages, SDK attach/resume), the server:

  1. Scans directories to find .jsonl files — O(N) readdir calls per session load, even though the indexer already knows the path
  2. Re-reads and re-parses unchanged .jsonl files on every request — no caching between consumers (WsHandler, timeline service)
  3. Makes N+1 HTTP requests for timeline pages — 1 request for the page metadata + N requests for individual turn bodies

The most impactful issue is #3: loading a 20-turn timeline page requires 21 HTTP round-trips.

Solution

Three-layer caching, all with optional injection (zero behavior change when deps aren't provided):

Layer 1 — O(1) Path Resolution

loadSessionHistory accepts an optional resolveFilePath callback that short-circuits the directory scan using the indexer's path map. Falls back to scan if resolver returns undefined.

Layer 2 — LRU Content Cache

New SessionContentCache class (~120 lines):

  • Stat-based invalidation (mtime + size) — no stale data
  • Request coalescing — concurrent reads share a single file read
  • Configurable byte budget (default 100MB, FRESHELL_SESSION_CACHE_MAX_MB env var)
  • LRU eviction using Map insertion-order iteration

Layer 3 — Timeline Body Batching

includeBodies query parameter on AgentTimelinePageQuery. When true, timeline page response includes full turn bodies inline, eliminating per-turn HTTP requests.

Impact

  • Layer 3 is the clear win — reduces 21 HTTP requests to 1 for a typical timeline page
  • Layer 2 is defensive — prevents redundant reads when multiple consumers load the same session
  • Layer 1 is a micro-optimization — saves a few readdirs on local filesystem

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions