Skip to content

perf(projects): bulk SQL replaces N+1 stats loop on /api/projects (26s → 3s cold)#65

Merged
0bserver07 merged 1 commit into
mainfrom
perf/projects-list-bulk-stats
May 2, 2026
Merged

perf(projects): bulk SQL replaces N+1 stats loop on /api/projects (26s → 3s cold)#65
0bserver07 merged 1 commit into
mainfrom
perf/projects-list-bulk-stats

Conversation

@0bserver07
Copy link
Copy Markdown
Owner

Summary

  • /api/projects?include_stats=true was the slow first paint on the dashboard. Took 26s cold on a 188-project / 228K-message store because of an N+1 per-project aggregator loop.
  • Three bulk SQL helpers in queries.py replace the loop. _dir_size_mb gets mtime-keyed memoization.

Numbers (real local data)

Before After
cold 26,152 ms 3,175 ms
warm 504 ms 443 ms

8.2× cold-load speedup. Warm is bounded by SQL (3 GROUP BYs on 228K messages); could be cached with TieredCache as a follow-up.

Test plan

  • pytest tests/ -q → 1333 passed, 2 skipped
  • sample stats payload still has the right shape (verified via TestClient)

🤖 Generated with Claude Code

The dashboard loads /api/projects?include_stats=true on first paint.
On a 188-project / 228K-message store this took 26s cold because
the route ran the per-project aggregator pipeline once per project.

This commit replaces that loop with three bulk passes:
- bulk_session_counts(): one GROUP BY for file_count
- bulk_project_lite_stats(): one GROUP BY for tokens / dates / message
  counts, sufficient for the project-list cards
- bulk_project_cost(): per-(project_id, model, speed) rollup folded
  through compute_cost(); honours fast-mode multipliers

Also memoises _dir_size_mb by (path, mtime) so repeat calls don't
re-glob 188 directories.

Result on the user's real store:
  cold:  26152 ms → 3175 ms  (8.2x)
  warm:    504 ms →  443 ms  (small; bounded by SQL pass)

The aggregator-only fields (avg_steps_per_command,
compact_summary_count, avg_tokens_per_command) default to 0 in the
list view — they're only meaningful in the per-project detail view,
which still runs the full aggregator on demand via /api/dashboard-data.

1333 backend tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@0bserver07 0bserver07 merged commit 5d5a5e8 into main May 2, 2026
9 checks passed
@0bserver07 0bserver07 deleted the perf/projects-list-bulk-stats branch May 2, 2026 13:47
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