Optimize usage dashboard: partial indexes, request cache, parallel histograms

- Add migration 14: partial covering indexes WHERE job_state='running'
  for user/project/subcluster groupings (tiny B-tree vs full table)
- Inline literal state value in BuildWhereClause so SQLite matches
  partial indexes instead of parameterized placeholders
- Add per-request statsGroupCache (sync.Once per filter+groupBy key)
  so identical grouped stats queries execute only once per GQL operation
- Parallelize 4 histogram queries in AddHistograms using errgroup
- Consolidate frontend from 6 GQL aliases to 2, sort+slice top-10
  client-side via $derived

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 5b26a6e5ff10
This commit is contained in:
2026-03-13 14:31:37 +01:00
parent cbe46c3524
commit d586fe4b43
10 changed files with 277 additions and 87 deletions

View File

@@ -198,8 +198,10 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select
}
if filter.State != nil {
if len(filter.State) == 1 {
// Inline literal value so SQLite can match partial indexes (WHERE job_state = 'running').
// Safe: values come from validated GraphQL enum (model.JobState).
singleStat := string(filter.State[0])
query = query.Where("job.job_state = ?", singleStat)
query = query.Where(fmt.Sprintf("job.job_state = '%s'", singleStat))
} else {
states := make([]string, len(filter.State))
for i, val := range filter.State {