Move failed count and avg duration from page-derived to backend stats
All stat card values now come from the /search/stats endpoint which queries the full time window, not just the current page of results. Consolidated into a single ClickHouse query for efficiency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -89,16 +89,18 @@ public class ClickHouseSearchEngine implements SearchEngine {
|
||||
|
||||
@Override
|
||||
public ExecutionStats stats(Instant from, Instant to) {
|
||||
Long p99 = jdbcTemplate.queryForObject(
|
||||
"SELECT toInt64(quantile(0.99)(duration_ms)) FROM route_executions " +
|
||||
"WHERE start_time >= ? AND start_time <= ?",
|
||||
Long.class, Timestamp.from(from), Timestamp.from(to));
|
||||
Long active = jdbcTemplate.queryForObject(
|
||||
"SELECT count() FROM route_executions WHERE status = 'RUNNING'",
|
||||
Long.class);
|
||||
return new ExecutionStats(
|
||||
p99 != null ? p99 : 0L,
|
||||
active != null ? active : 0L);
|
||||
return jdbcTemplate.queryForObject(
|
||||
"SELECT countIf(status = 'FAILED') AS failed_count, " +
|
||||
"toInt64(avg(duration_ms)) AS avg_duration_ms, " +
|
||||
"toInt64(quantile(0.99)(duration_ms)) AS p99_duration_ms, " +
|
||||
"countIf(status = 'RUNNING') AS active_count " +
|
||||
"FROM route_executions WHERE start_time >= ? AND start_time <= ?",
|
||||
(rs, rowNum) -> new ExecutionStats(
|
||||
rs.getLong("failed_count"),
|
||||
rs.getLong("avg_duration_ms"),
|
||||
rs.getLong("p99_duration_ms"),
|
||||
rs.getLong("active_count")),
|
||||
Timestamp.from(from), Timestamp.from(to));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.cameleer3.server.core.search;
|
||||
|
||||
/**
|
||||
* Aggregate execution statistics.
|
||||
* Aggregate execution statistics within a time window.
|
||||
*
|
||||
* @param p99LatencyMs 99th percentile duration in milliseconds within the requested time window
|
||||
* @param activeCount number of currently running executions
|
||||
* @param failedCount number of failed executions
|
||||
* @param avgDurationMs average duration in milliseconds
|
||||
* @param p99LatencyMs 99th percentile duration in milliseconds
|
||||
* @param activeCount number of currently running executions
|
||||
*/
|
||||
public record ExecutionStats(long p99LatencyMs, long activeCount) {}
|
||||
public record ExecutionStats(long failedCount, long avgDurationMs, long p99LatencyMs, long activeCount) {}
|
||||
|
||||
@@ -1049,6 +1049,14 @@
|
||||
"ExecutionStats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failedCount": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"avgDurationMs": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"p99LatencyMs": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
|
||||
2
ui/src/api/schema.d.ts
vendored
2
ui/src/api/schema.d.ts
vendored
@@ -217,6 +217,8 @@ export interface ProcessorNode {
|
||||
export type ProcessorSnapshot = Record<string, string>;
|
||||
|
||||
export interface ExecutionStats {
|
||||
failedCount: number;
|
||||
avgDurationMs: number;
|
||||
p99LatencyMs: number;
|
||||
activeCount: number;
|
||||
}
|
||||
|
||||
@@ -24,12 +24,6 @@ export function ExecutionExplorer() {
|
||||
const total = data?.total ?? 0;
|
||||
const results = data?.data ?? [];
|
||||
|
||||
// Derive stats from current search results
|
||||
const failedCount = results.filter((r) => r.status === 'FAILED').length;
|
||||
const avgDuration = results.length > 0
|
||||
? Math.round(results.reduce((sum, r) => sum + r.durationMs, 0) / results.length)
|
||||
: 0;
|
||||
|
||||
const showFrom = total > 0 ? offset + 1 : 0;
|
||||
const showTo = Math.min(offset + limit, total);
|
||||
|
||||
@@ -50,8 +44,8 @@ export function ExecutionExplorer() {
|
||||
{/* Stats Bar */}
|
||||
<div className={styles.statsBar}>
|
||||
<StatCard label="Total Matches" value={total.toLocaleString()} accent="amber" change={`from current search`} sparkData={sparkTotal} />
|
||||
<StatCard label="Avg Duration" value={`${avgDuration.toLocaleString()}ms`} accent="cyan" sparkData={sparkAvgDuration} />
|
||||
<StatCard label="Failed (page)" value={failedCount.toLocaleString()} accent="rose" sparkData={sparkFailed} />
|
||||
<StatCard label="Avg Duration" value={stats ? `${stats.avgDurationMs.toLocaleString()}ms` : '--'} accent="cyan" sparkData={sparkAvgDuration} />
|
||||
<StatCard label="Failed" value={stats ? stats.failedCount.toLocaleString() : '--'} accent="rose" sparkData={sparkFailed} />
|
||||
<StatCard label="P99 Latency" value={stats ? `${stats.p99LatencyMs.toLocaleString()}ms` : '--'} accent="green" sparkData={sparkP99} />
|
||||
<StatCard label="In-Flight" value={stats ? stats.activeCount.toLocaleString() : '--'} accent="blue" change="running executions" sparkData={sparkActive} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user