Skip to content

Commit d349f18

Browse files
adham90claude
andcommitted
Optimize dashboard queries: ~75 SQL queries down to ~10
Replace Ruby-level aggregation with SQL GROUP BY and conditional aggregation (SUM CASE WHEN) across all dashboard analytics methods. Chart data no longer loads all records into memory. Key changes: - now_strip_data: ~15 queries → 3 (pick with conditional aggregation) - Chart builders: load-all-records → 1 SQL GROUP BY per chart - build_hourly_activity_data: 48 queries → 1 - model_stats: 5 queries → 1 - batch_agent_stats: 4 queries → 1 - cache_savings: 3 queries → 1 - Add composite indexes: [status, created_at], [model_id, status], [cache_hit, created_at] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f523ebc commit d349f18

6 files changed

Lines changed: 342 additions & 191 deletions

File tree

app/models/ruby_llm/agents/execution.rb

Lines changed: 72 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ def tool_calls?
275275
alias_method :has_tool_calls?, :tool_calls?
276276

277277
# Returns real-time dashboard data for the Now Strip
278+
# Optimized: 3 queries (current aggregate + previous aggregate + running count)
279+
# instead of ~15 individual count/sum/average queries.
278280
#
279281
# @param range [String] Time range: "today", "7d", "30d", or "90d"
280282
# @return [Hash] Now strip metrics with period-over-period comparisons
@@ -293,38 +295,31 @@ def self.now_strip_data(range: "today")
293295
else yesterday
294296
end
295297

296-
current = {
297-
running: running.count,
298-
success_today: current_scope.status_success.count,
299-
errors_today: current_scope.status_error.count,
300-
timeouts_today: current_scope.status_timeout.count,
301-
cost_today: current_scope.sum(:total_cost) || 0,
302-
executions_today: current_scope.count,
303-
success_rate: calculate_period_success_rate(current_scope),
304-
avg_duration_ms: current_scope.avg_duration&.round || 0,
305-
total_tokens: current_scope.total_tokens_sum || 0
306-
}
307-
308-
previous = {
309-
success: previous_scope.status_success.count,
310-
errors: previous_scope.status_error.count,
311-
cost: previous_scope.sum(:total_cost) || 0,
312-
avg_duration_ms: previous_scope.avg_duration&.round || 0,
313-
total_tokens: previous_scope.total_tokens_sum || 0
314-
}
298+
curr = aggregate_period_stats(current_scope)
299+
prev = aggregate_period_stats(previous_scope)
315300

316-
current.merge(
301+
{
302+
running: running.count,
303+
success_today: curr[:success],
304+
errors_today: curr[:errors],
305+
timeouts_today: curr[:timeouts],
306+
cost_today: curr[:cost],
307+
executions_today: curr[:total],
308+
success_rate: curr[:success_rate],
309+
avg_duration_ms: curr[:avg_duration_ms],
310+
total_tokens: curr[:tokens],
317311
comparisons: {
318-
success_change: pct_change(previous[:success], current[:success_today]),
319-
errors_change: pct_change(previous[:errors], current[:errors_today]),
320-
cost_change: pct_change(previous[:cost], current[:cost_today]),
321-
duration_change: pct_change(previous[:avg_duration_ms], current[:avg_duration_ms]),
322-
tokens_change: pct_change(previous[:total_tokens], current[:total_tokens])
312+
success_change: pct_change(prev[:success], curr[:success]),
313+
errors_change: pct_change(prev[:errors], curr[:errors]),
314+
cost_change: pct_change(prev[:cost], curr[:cost]),
315+
duration_change: pct_change(prev[:avg_duration_ms], curr[:avg_duration_ms]),
316+
tokens_change: pct_change(prev[:tokens], curr[:tokens])
323317
}
324-
)
318+
}
325319
end
326320

327321
# Returns Now Strip data for a custom date range
322+
# Optimized: 3 queries instead of ~15.
328323
#
329324
# Compares the selected range against the same-length window
330325
# immediately preceding it.
@@ -339,35 +334,27 @@ def self.now_strip_data_for_dates(from:, to:)
339334
previous_to = from - 1.day
340335
previous_scope = where(created_at: previous_from.beginning_of_day..previous_to.end_of_day)
341336

342-
current = {
343-
running: running.count,
344-
success_today: current_scope.status_success.count,
345-
errors_today: current_scope.status_error.count,
346-
timeouts_today: current_scope.status_timeout.count,
347-
cost_today: current_scope.sum(:total_cost) || 0,
348-
executions_today: current_scope.count,
349-
success_rate: calculate_period_success_rate(current_scope),
350-
avg_duration_ms: current_scope.avg_duration&.round || 0,
351-
total_tokens: current_scope.total_tokens_sum || 0
352-
}
353-
354-
previous = {
355-
success: previous_scope.status_success.count,
356-
errors: previous_scope.status_error.count,
357-
cost: previous_scope.sum(:total_cost) || 0,
358-
avg_duration_ms: previous_scope.avg_duration&.round || 0,
359-
total_tokens: previous_scope.total_tokens_sum || 0
360-
}
337+
curr = aggregate_period_stats(current_scope)
338+
prev = aggregate_period_stats(previous_scope)
361339

362-
current.merge(
340+
{
341+
running: running.count,
342+
success_today: curr[:success],
343+
errors_today: curr[:errors],
344+
timeouts_today: curr[:timeouts],
345+
cost_today: curr[:cost],
346+
executions_today: curr[:total],
347+
success_rate: curr[:success_rate],
348+
avg_duration_ms: curr[:avg_duration_ms],
349+
total_tokens: curr[:tokens],
363350
comparisons: {
364-
success_change: pct_change(previous[:success], current[:success_today]),
365-
errors_change: pct_change(previous[:errors], current[:errors_today]),
366-
cost_change: pct_change(previous[:cost], current[:cost_today]),
367-
duration_change: pct_change(previous[:avg_duration_ms], current[:avg_duration_ms]),
368-
tokens_change: pct_change(previous[:total_tokens], current[:total_tokens])
351+
success_change: pct_change(prev[:success], curr[:success]),
352+
errors_change: pct_change(prev[:errors], curr[:errors]),
353+
cost_change: pct_change(prev[:cost], curr[:cost]),
354+
duration_change: pct_change(prev[:avg_duration_ms], curr[:avg_duration_ms]),
355+
tokens_change: pct_change(prev[:tokens], curr[:tokens])
369356
}
370-
)
357+
}
371358
end
372359

373360
# Calculates percentage change between old and new values
@@ -391,6 +378,39 @@ def self.calculate_period_success_rate(scope)
391378
(scope.successful.count.to_f / total * 100).round(1)
392379
end
393380

381+
# Returns aggregate stats for a scope in a single query using conditional aggregation
382+
#
383+
# Replaces ~9 individual count/sum/average queries with one SQL query.
384+
#
385+
# @param scope [ActiveRecord::Relation] Time-filtered scope
386+
# @return [Hash] Aggregated metrics
387+
def self.aggregate_period_stats(scope)
388+
total, success, errors, timeouts, cost, avg_dur, tokens = scope.pick(
389+
Arel.sql("COUNT(*)"),
390+
Arel.sql("SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END)"),
391+
Arel.sql("SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END)"),
392+
Arel.sql("SUM(CASE WHEN status = 'timeout' THEN 1 ELSE 0 END)"),
393+
Arel.sql("COALESCE(SUM(total_cost), 0)"),
394+
Arel.sql("AVG(duration_ms)"),
395+
Arel.sql("COALESCE(SUM(total_tokens), 0)")
396+
)
397+
398+
total = total.to_i
399+
success = success.to_i
400+
401+
{
402+
total: total,
403+
success: success,
404+
errors: errors.to_i,
405+
timeouts: timeouts.to_i,
406+
cost: cost.to_f,
407+
avg_duration_ms: avg_dur.to_i,
408+
tokens: tokens.to_i,
409+
success_rate: (total > 0) ? (success.to_f / total * 100).round(1) : 0.0
410+
}
411+
end
412+
private_class_method :aggregate_period_stats
413+
394414
private
395415

396416
# Calculates and sets total_tokens from input and output

0 commit comments

Comments
 (0)