more features

This commit is contained in:
Para Dox
2025-05-28 21:55:32 +07:00
parent 6055d17a4d
commit 36cc615903

View File

@@ -60,6 +60,8 @@ type StatsCollector struct {
requestStats []ResponseStats requestStats []ResponseStats
methodStats map[string][]time.Duration // Track durations by method methodStats map[string][]time.Duration // Track durations by method
backendMethodStats map[string]map[string][]time.Duration // Track durations by backend and method backendMethodStats map[string]map[string][]time.Duration // Track durations by backend and method
backendWins map[string]int // Track how many times each backend responded first
methodBackendWins map[string]map[string]int // Track wins per method per backend
totalRequests int totalRequests int
errorCount int errorCount int
wsConnections []WebSocketStats // Track websocket connections wsConnections []WebSocketStats // Track websocket connections
@@ -79,6 +81,8 @@ func NewStatsCollector(summaryInterval time.Duration) *StatsCollector {
requestStats: make([]ResponseStats, 0, 1000), requestStats: make([]ResponseStats, 0, 1000),
methodStats: make(map[string][]time.Duration), methodStats: make(map[string][]time.Duration),
backendMethodStats: make(map[string]map[string][]time.Duration), backendMethodStats: make(map[string]map[string][]time.Duration),
backendWins: make(map[string]int),
methodBackendWins: make(map[string]map[string]int),
appStartTime: now, appStartTime: now,
intervalStartTime: now, intervalStartTime: now,
summaryInterval: summaryInterval, summaryInterval: summaryInterval,
@@ -174,6 +178,30 @@ func (sc *StatsCollector) AddStats(stats []ResponseStats, totalDuration time.Dur
sc.mu.Lock() sc.mu.Lock()
defer sc.mu.Unlock() defer sc.mu.Unlock()
// Find the fastest successful response
var fastestBackend string
var fastestDuration time.Duration = time.Hour // Initialize with a very large duration
var method string
for _, stat := range stats {
if stat.Error == nil && stat.Duration < fastestDuration {
fastestDuration = stat.Duration
fastestBackend = stat.Backend
method = stat.Method
}
}
// Track the win if we found a successful response
if fastestBackend != "" {
sc.backendWins[fastestBackend]++
// Track wins per method
if _, exists := sc.methodBackendWins[method]; !exists {
sc.methodBackendWins[method] = make(map[string]int)
}
sc.methodBackendWins[method][fastestBackend]++
}
// Add stats to the collection // Add stats to the collection
for _, stat := range stats { for _, stat := range stats {
sc.requestStats = append(sc.requestStats, stat) sc.requestStats = append(sc.requestStats, stat)
@@ -446,6 +474,34 @@ func (sc *StatsCollector) printSummary() {
formatDuration(p50), formatDuration(p90), formatDuration(p99)) formatDuration(p50), formatDuration(p90), formatDuration(p99))
} }
// Print backend wins statistics
fmt.Printf("\nBackend First Response Wins:\n")
fmt.Printf("%-20s %10s %10s\n", "Backend", "Wins", "Win %")
fmt.Printf("%s\n", strings.Repeat("-", 42))
totalWins := 0
for _, wins := range sc.backendWins {
totalWins += wins
}
// Sort backends by wins for consistent output
type backendWin struct {
backend string
wins int
}
var winList []backendWin
for backend, wins := range sc.backendWins {
winList = append(winList, backendWin{backend, wins})
}
sort.Slice(winList, func(i, j int) bool {
return winList[i].wins > winList[j].wins
})
for _, bw := range winList {
winPercentage := float64(bw.wins) / float64(totalWins) * 100
fmt.Printf("%-20s %10d %9.1f%%\n", bw.backend, bw.wins, winPercentage)
}
// Print per-method statistics // Print per-method statistics
if len(sc.methodStats) > 0 { if len(sc.methodStats) > 0 {
fmt.Printf("\nPer-Method Statistics (Primary Backend):\n") fmt.Printf("\nPer-Method Statistics (Primary Backend):\n")
@@ -539,6 +595,35 @@ func (sc *StatsCollector) printSummary() {
method := methodList[i].method method := methodList[i].method
fmt.Printf("\n Method: %s (Total requests: %d)\n", method, methodList[i].count) fmt.Printf("\n Method: %s (Total requests: %d)\n", method, methodList[i].count)
// Show wins for this method if available
if methodWins, exists := sc.methodBackendWins[method]; exists {
fmt.Printf(" First Response Wins: ")
totalMethodWins := 0
for _, wins := range methodWins {
totalMethodWins += wins
}
// Sort backends by wins for this method
var methodWinList []backendWin
for backend, wins := range methodWins {
methodWinList = append(methodWinList, backendWin{backend, wins})
}
sort.Slice(methodWinList, func(i, j int) bool {
return methodWinList[i].wins > methodWinList[j].wins
})
// Print wins inline
for idx, bw := range methodWinList {
if idx > 0 {
fmt.Printf(", ")
}
winPercentage := float64(bw.wins) / float64(totalMethodWins) * 100
fmt.Printf("%s: %d (%.1f%%)", bw.backend, bw.wins, winPercentage)
}
fmt.Printf("\n")
}
fmt.Printf(" %-20s %10s %10s %10s %10s %10s %10s %10s\n", fmt.Printf(" %-20s %10s %10s %10s %10s %10s %10s %10s\n",
"Backend", "Count", "Min", "Avg", "Max", "p50", "p90", "p99") "Backend", "Count", "Min", "Avg", "Max", "p50", "p90", "p99")
fmt.Printf(" %s\n", strings.Repeat("-", 98)) fmt.Printf(" %s\n", strings.Repeat("-", 98))
@@ -625,6 +710,10 @@ func (sc *StatsCollector) printSummary() {
} }
} }
// Reset backend wins statistics
sc.backendWins = make(map[string]int)
sc.methodBackendWins = make(map[string]map[string]int)
// Reset CU counters for the next interval // Reset CU counters for the next interval
sc.totalCU = 0 sc.totalCU = 0
sc.methodCU = make(map[string]int) sc.methodCU = make(map[string]int)