Skip to content

perf(hub): cache sessionAgentIDs on archived sessions (#118 §1)#295

Merged
physercoe merged 1 commit into
mainfrom
perf/118-session-agent-ids-denorm
Jun 27, 2026
Merged

perf(hub): cache sessionAgentIDs on archived sessions (#118 §1)#295
physercoe merged 1 commit into
mainfrom
perf/118-session-agent-ids-denorm

Conversation

@physercoe

Copy link
Copy Markdown
Owner

First of three Tier-A perf PRs for #118 (Insight transcript ~10s load on 10k-event sessions). Verified the issue's analysis against the tree — all bottlenecks intact — and split the safe wins into separate PRs.

Problem (#118 bottleneck #2)

sessionAgentIDs runs GROUP BY agent_id ORDER BY MIN(ts) over a session's full agent_events span on every digest/turns read — even when the digest is already fresh. For a 10k-event session that's a 10k-row index scan per Insight open, and it's called from the digest handler, the turns handler, and OTLP export.

Fix

Denormalize the result onto a new sessions.agent_ids_json column, materialized lazily and only for terminal (archived) sessions whose agent set can no longer grow.

  • A paused session can resume and bind a new agent (handlers_sessions.go:705), so non-archived sessions keep scanning authoritatively → byte-identical behaviour for live/paused sessions.
  • archived is terminal (no transition back), so its agent set is immutable and safe to cache.
  • Read-repair writes the set back on the first archived read; a malformed/empty blob falls through to the scan.

Changes

  • migration 0060_sessions_agent_ids: sessions.agent_ids_json TEXT (nullable)
  • sessionAgentIDs: archived + cached → O(1) JSON parse; else scan (+ write-back when archived)
  • test TestSessionAgentIDs_CachesOnlyWhenArchived: not cached while active; materialized + served-from-cache once archived (a later out-of-band event is not re-scanned, proving the O(1) path)

Verification

go build, go vet, gofmt clean; digest/turns/insights/OTLP/migration suites pass locally.

Part of #118 (Tier A). Follow-ups: digest read-cache (§2-cache), fold-on-session-close.

🤖 Generated with Claude Code

sessionAgentIDs ran `GROUP BY agent_id ORDER BY MIN(ts)` over a session's
full agent_events span on *every* digest/turns read — even when the digest
was already fresh. For a 10k-event session that is a 10k-row index scan per
Insight open (#118 bottleneck #2).

Denormalize the result onto a new `sessions.agent_ids_json` column,
materialized lazily and ONLY for terminal (archived) sessions whose agent
set can no longer grow. A paused session can resume and bind a new agent, so
non-archived sessions keep scanning authoritatively — behaviour is
byte-identical for live/paused sessions and a malformed/empty blob falls
through to the scan. Read-repair writes the set back on the first archived
read.

- migration 0060: `sessions.agent_ids_json TEXT` (nullable)
- sessionAgentIDs: archived + cached -> O(1) parse; else scan (+ write-back)
- test: scan-not-cached while active; materialize + served-from-cache once
  archived (a later out-of-band event is NOT re-scanned)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@physercoe physercoe merged commit 1740c9b into main Jun 27, 2026
4 checks passed
@physercoe physercoe deleted the perf/118-session-agent-ids-denorm branch June 27, 2026 12:58
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