more features
This commit is contained in:
@@ -1216,6 +1216,7 @@ func handleRequest(w http.ResponseWriter, r *http.Request, backends []Backend, c
|
|||||||
|
|
||||||
// Track if we've already sent a response
|
// Track if we've already sent a response
|
||||||
var responseHandled atomic.Bool
|
var responseHandled atomic.Bool
|
||||||
|
var firstBackendStartTime atomic.Pointer[time.Time]
|
||||||
|
|
||||||
for _, backend := range backends {
|
for _, backend := range backends {
|
||||||
// Skip secondary backends for stateful methods
|
// Skip secondary backends for stateful methods
|
||||||
@@ -1237,6 +1238,12 @@ func handleRequest(w http.ResponseWriter, r *http.Request, backends []Backend, c
|
|||||||
// Track when this goroutine actually starts processing
|
// Track when this goroutine actually starts processing
|
||||||
goroutineStartTime := time.Now()
|
goroutineStartTime := time.Now()
|
||||||
|
|
||||||
|
// Record the first backend start time (should be primary)
|
||||||
|
if b.Role == "primary" {
|
||||||
|
t := goroutineStartTime
|
||||||
|
firstBackendStartTime.Store(&t)
|
||||||
|
}
|
||||||
|
|
||||||
// If this is a secondary backend, wait for p50 delay
|
// If this is a secondary backend, wait for p50 delay
|
||||||
if b.Role != "primary" {
|
if b.Role != "primary" {
|
||||||
delayTimer := time.NewTimer(p50Delay)
|
delayTimer := time.NewTimer(p50Delay)
|
||||||
@@ -1332,15 +1339,18 @@ func handleRequest(w http.ResponseWriter, r *http.Request, backends []Backend, c
|
|||||||
err error
|
err error
|
||||||
body []byte
|
body []byte
|
||||||
}
|
}
|
||||||
|
var responseReceivedTime time.Time
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case response = <-responseChan:
|
case response = <-responseChan:
|
||||||
// Got a response
|
// Got a response
|
||||||
|
responseReceivedTime = time.Now()
|
||||||
case <-time.After(30 * time.Second):
|
case <-time.After(30 * time.Second):
|
||||||
// Timeout
|
// Timeout
|
||||||
if !responseHandled.CompareAndSwap(false, true) {
|
if !responseHandled.CompareAndSwap(false, true) {
|
||||||
// Someone else handled it
|
// Someone else handled it
|
||||||
response = <-responseChan
|
response = <-responseChan
|
||||||
|
responseReceivedTime = time.Now()
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, "Timeout waiting for any backend", http.StatusGatewayTimeout)
|
http.Error(w, "Timeout waiting for any backend", http.StatusGatewayTimeout)
|
||||||
// Always wait for primary backend to complete before collecting stats
|
// Always wait for primary backend to complete before collecting stats
|
||||||
@@ -1395,11 +1405,20 @@ func handleRequest(w http.ResponseWriter, r *http.Request, backends []Backend, c
|
|||||||
// Find the stat for the winning backend and update it with the actual user-experienced duration
|
// Find the stat for the winning backend and update it with the actual user-experienced duration
|
||||||
for i := range stats {
|
for i := range stats {
|
||||||
if stats[i].Backend == response.backend && stats[i].Error == nil {
|
if stats[i].Backend == response.backend && stats[i].Error == nil {
|
||||||
|
// Calculate user latency from when the first backend started processing
|
||||||
|
var userLatency time.Duration
|
||||||
|
if firstStart := firstBackendStartTime.Load(); firstStart != nil && !responseReceivedTime.IsZero() {
|
||||||
|
userLatency = responseReceivedTime.Sub(*firstStart)
|
||||||
|
} else {
|
||||||
|
// Fallback to original calculation if somehow we don't have the times
|
||||||
|
userLatency = time.Since(startTime)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a special stat entry for the actual first response time
|
// Create a special stat entry for the actual first response time
|
||||||
actualFirstResponseStat := ResponseStats{
|
actualFirstResponseStat := ResponseStats{
|
||||||
Backend: "actual-first-response",
|
Backend: "actual-first-response",
|
||||||
StatusCode: stats[i].StatusCode,
|
StatusCode: stats[i].StatusCode,
|
||||||
Duration: time.Since(startTime), // This is what the user actually experienced
|
Duration: userLatency,
|
||||||
Error: nil,
|
Error: nil,
|
||||||
Method: stats[i].Method,
|
Method: stats[i].Method,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user