Skip to content

perf: cut slow pages and APIs found in Sentry traces - #92

Merged
SushantGautam merged 2 commits into
mainfrom
perf/scenario-result-summary-columns
Nov 11, 2026
Merged

SushantGautam merged 2 commits into
mainfrom
perf/scenario-result-summary-columns

Conversation

@SushantGautam

Copy link
Copy Markdown
Collaborator

Production traces (Sentry, 14 days) showed /experiments/{id}/ at p95 9.3 s, /runs/data/ at p95 3.1 s (the dashboard 502) and /experiments/new/ at p95 3.4 s. Span data put almost all of it on one query: loading every ScenarioResult.result transcript to read a severity. Full audit, evidence and status: docs/performance-audit.md.

Data layer

  • ScenarioResult keeps trial counts and a small list_summary projection, kept in step by save(). Migration audits/0011 backfills them in batches. pass_counts becomes one GROUP BY; the run Results list, CSV export and compare read the projection.
  • Indexes: AuditRun, AuditEvent, ScenarioResult, OtlpSpan (audits/0012, model_registry/0023).
  • AuditEvent: batched turn-event inserts; hourly pruning of per-turn events of runs finished more than SIMPLEAUDIT_EVENT_RETENTION_DAYS (30) ago; manage.py prune_audit_events.

Web

  • One membership query per request; token-auth requests no longer write sessions.
  • SSE: per-user and per-process stream caps (429 + browser retry), idle backoff, keepalive, end for finished runs.
  • API: run list and results paginated (limit/offset, {count, next, previous, results}); run-list rows are slim, ?expand=snapshots restores the full shape. Breaking for external API clients of these two endpoints.
  • Streamed exports; throttled Agent-page Open WebUI sync; on-demand scenario edit forms; deferred JSON on compare/experiments/runs grid; one-aggregate worker counters; concurrent probes; Caddy compression and upstream timeouts.

Upgrade notes

  • Run migrations in a quiet window: audits/0011 reads every stored result once; the new indexes lock their tables briefly.
  • New settings (all optional): SIMPLEAUDIT_SSE_MAX_STREAMS_PER_USER, SIMPLEAUDIT_SSE_MAX_STREAMS, SIMPLEAUDIT_EVENT_RETENTION_DAYS, SIMPLEAUDIT_OPENWEBUI_SYNC_TTL_S (documented in docs/deployment.md and the .env examples).

Checks

  • pytest -m "not embedded_hatchet": 1810 passed after merging main (the 126 collection errors are pre-existing duplicates under .claude/worktrees/).
  • pytest -m embedded_hatchet (worker changed): passed. ruff check . clean, makemigrations --check clean.
  • Scenarios page lazy edit form checked in a browser. Not run against Postgres; Caddyfile not validated (caddy not installed locally).

Production traces showed /experiments/{id}/ (p95 9.3 s), /runs/data/
(p95 3.1 s, the 502) and /experiments/new/ dominated by loading every
ScenarioResult.result transcript to read a severity.

Data layer
- ScenarioResult keeps trial counts (n_pass, n_judged, n_errors,
  n_ungraded) and a small list_summary projection, kept in step by save();
  migration 0011 backfills them in batches. pass_counts is one GROUP BY;
  the run Results list, CSV export and compare read the projection.
- Indexes: AuditRun (project/archived/created_at, status/archived,
  monitor/created_at, started_at), AuditEvent (run/kind/item; drop three
  single-column ones), ScenarioResult (run/status), OtlpSpan (received_at).
- AuditEvent: turn events are bulk-inserted per batch; the sweeper prunes
  per-turn events of runs finished > SIMPLEAUDIT_EVENT_RETENTION_DAYS (30);
  manage.py prune_audit_events.

Web
- One membership query per request feeds access, admin flag, workspace
  switcher and can_write; token-auth requests no longer write sessions.
- SSE: per-user and per-process stream caps (429 + retry), idle backoff,
  keepalive, and an end for finished runs missing a terminal event.
- API: run list and results are paginated (limit/offset); run-list rows are
  slim, ?expand=snapshots restores the full shape. Event poll capped at 1000.
- Exports stream (run JSON/CSV, runs CSV, API export encoding).
- Agent page Open WebUI sync throttled (SIMPLEAUDIT_OPENWEBUI_SYNC_TTL_S);
  scenarios edit forms load on demand; compare/experiment/runs grid defer
  unused JSON; worker counters are one aggregate; concurrent model and
  health probes; DB-side prompt-version check; static Cache-Control;
  Caddy compression and upstream timeouts.

docs/performance-audit.md records the audit, the Sentry evidence and status.
…summary-columns

# Conflicts:
#	infra/ui.py
#	infra/worker.py
Copilot AI balanced review requested due to automatic review settings November 11, 2026 19:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@SushantGautam
SushantGautam merged commit 522973c into main Nov 11, 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.

2 participants