From d07ee4090c43d973d187bc51c0ec5399530da263 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sat, 29 Nov 2025 15:53:05 +0000 Subject: [PATCH] Dashboard update. --- admin/dashboard.html | 79 ++++++++++++++++++++++++++++++++----------- metrics_aggregator.go | 17 ++++++++++ 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/admin/dashboard.html b/admin/dashboard.html index cfb1b3a..4e3783d 100644 --- a/admin/dashboard.html +++ b/admin/dashboard.html @@ -1277,15 +1277,34 @@ stateEl.classList.remove('loading'); let badgeClass = 'badge-info'; - if (data.state === 'closed') badgeClass = 'badge-success'; - else if (data.state === 'open') badgeClass = 'badge-danger'; - else if (data.state === 'half-open') badgeClass = 'badge-warning'; + let stateText = data.state || 'unknown'; - stateEl.innerHTML = `${data.state || 'Unknown'}`; + // For cluster mode, determine state from instance counts + if (data.instances_open !== undefined) { + // Cluster mode data + if (data.instances_open > 0) { + stateText = `${data.instances_open} open`; + badgeClass = 'badge-danger'; + } else if (data.instances_halfopen > 0) { + stateText = `${data.instances_halfopen} half-open`; + badgeClass = 'badge-warning'; + } else if (data.instances_closed > 0) { + stateText = `${data.instances_closed} closed`; + badgeClass = 'badge-success'; + } + } else { + // Single instance mode + if (data.state === 'closed') badgeClass = 'badge-success'; + else if (data.state === 'open') badgeClass = 'badge-danger'; + else if (data.state === 'half-open') badgeClass = 'badge-warning'; + } + + stateEl.innerHTML = `${stateText}`; document.getElementById('cb-enabled').textContent = data.enabled ? 'Yes' : 'No'; if (data.counts) { + // Single instance mode with detailed counts document.getElementById('cb-total-requests').textContent = (data.counts.requests || 0).toLocaleString(); document.getElementById('cb-total-successes').textContent = @@ -1296,6 +1315,14 @@ (data.counts.consecutive_successes || 0).toLocaleString(); document.getElementById('cb-consecutive-failures').textContent = (data.counts.consecutive_failures || 0).toLocaleString(); + } else if (data.instances_open !== undefined) { + // Cluster mode - show instance distribution instead + const total = (data.instances_open || 0) + (data.instances_closed || 0) + (data.instances_halfopen || 0); + document.getElementById('cb-total-requests').textContent = total + ' instances'; + document.getElementById('cb-total-successes').textContent = (data.instances_closed || 0).toLocaleString(); + document.getElementById('cb-total-failures').textContent = (data.instances_open || 0).toLocaleString(); + document.getElementById('cb-consecutive-successes').textContent = '--'; + document.getElementById('cb-consecutive-failures').textContent = '--'; } if (data.config) { @@ -1307,23 +1334,31 @@ function updateCoalescing(data) { document.getElementById('coalescing-rate').textContent = (data.backend_savings_pct || 0).toFixed(1) + '%'; - document.getElementById('coalescing-total').textContent = - (data.total_requests || 0).toLocaleString(); - document.getElementById('coalescing-primary').textContent = - (data.primary_requests || 0).toLocaleString(); - document.getElementById('coalescing-coalesced').textContent = - (data.coalesced_requests || 0).toLocaleString(); + + // Handle both single instance (total_requests) and cluster mode (total_coalesced + total_primary) + const totalRequests = data.total_requests || + ((data.total_coalesced_requests || 0) + (data.total_primary_requests || 0)); + document.getElementById('coalescing-total').textContent = totalRequests.toLocaleString(); + + // Handle both single instance and cluster mode field names + const primaryRequests = data.primary_requests || data.total_primary_requests || 0; + document.getElementById('coalescing-primary').textContent = primaryRequests.toLocaleString(); + + const coalescedRequests = data.coalesced_requests || data.total_coalesced_requests || 0; + document.getElementById('coalescing-coalesced').textContent = coalescedRequests.toLocaleString(); + document.getElementById('coalescing-savings').textContent = (data.backend_savings_pct || 0).toFixed(1) + '%'; } function updateRetryBudget(data) { - document.getElementById('retry-tokens').textContent = - data.current_tokens || '--'; - document.getElementById('retry-current-tokens').textContent = - data.current_tokens || '--'; - document.getElementById('retry-max-tokens').textContent = - data.max_tokens || '--'; + // Use explicit undefined check to handle 0 values correctly + const currentTokens = data.current_tokens !== undefined ? data.current_tokens : '--'; + const maxTokens = data.max_tokens !== undefined ? data.max_tokens : '--'; + + document.getElementById('retry-tokens').textContent = currentTokens; + document.getElementById('retry-current-tokens').textContent = currentTokens; + document.getElementById('retry-max-tokens').textContent = maxTokens; document.getElementById('retry-total').textContent = (data.total_attempts || 0).toLocaleString(); document.getElementById('retry-denied').textContent = @@ -1333,13 +1368,17 @@ } function updateWebSocket(data) { - document.getElementById('ws-connections').textContent = - data.active_connections || 0; + // Handle both single instance (active_connections) and cluster mode (total_connections) + const connections = data.active_connections !== undefined ? data.active_connections : + (data.total_connections !== undefined ? data.total_connections : 0); + document.getElementById('ws-connections').textContent = connections; } function updateConnections(data) { - document.getElementById('pool-connections').textContent = - data.active_connections || 0; + // Handle both single instance (active_connections) and cluster mode (total_active) + const connections = data.active_connections !== undefined ? data.active_connections : + (data.total_active !== undefined ? data.total_active : 0); + document.getElementById('pool-connections').textContent = connections; } async function resetCoalescing() { diff --git a/metrics_aggregator.go b/metrics_aggregator.go index 4734647..eb68c93 100644 --- a/metrics_aggregator.go +++ b/metrics_aggregator.go @@ -519,7 +519,10 @@ func (ma *MetricsAggregator) aggregateStats(instances []InstanceMetrics) map[str totalRetryAllowed int64 totalRetryDenied int64 totalRetryAttempts int64 + totalCurrentTokens int64 + totalMaxTokens int64 retryBudgetEnabled = false + retryTokensPerSec float64 // Use max tokens_per_sec from any instance // Circuit breaker stats cbOpenCount int @@ -645,6 +648,17 @@ func (ma *MetricsAggregator) aggregateStats(instances []InstanceMetrics) map[str if attempts, ok := instance.RetryBudget["total_attempts"].(float64); ok { totalRetryAttempts += int64(attempts) } + if currentTokens, ok := instance.RetryBudget["current_tokens"].(float64); ok { + totalCurrentTokens += int64(currentTokens) + } + if maxTokens, ok := instance.RetryBudget["max_tokens"].(float64); ok { + totalMaxTokens += int64(maxTokens) + } + if tokensPerSec, ok := instance.RetryBudget["tokens_per_sec"].(float64); ok { + if tokensPerSec > retryTokensPerSec { + retryTokensPerSec = tokensPerSec + } + } } // Aggregate circuit breaker stats @@ -748,6 +762,9 @@ func (ma *MetricsAggregator) aggregateStats(instances []InstanceMetrics) map[str "denied_retries": totalRetryDenied, "total_attempts": totalRetryAttempts, "denial_rate_pct": retryDenialRate, + "current_tokens": totalCurrentTokens, + "max_tokens": totalMaxTokens, + "tokens_per_sec": retryTokensPerSec, }, "circuit_breaker": map[string]interface{}{ "enabled": circuitBreakerEnabled,