proxy-pool/internal/loadgen/acceptance.go

68 lines
2.4 KiB
Go

package loadgen
import (
"errors"
"fmt"
"math"
"time"
)
var ErrInvalidAcceptanceCriteria = errors.New("invalid load generator acceptance criteria")
// AcceptanceCriteria describes optional report gates. The explicit enforcement
// flags keep a zero-error requirement distinct from an omitted requirement.
type AcceptanceCriteria struct {
EnforceFailureRate bool
MaxFailureRate float64
EnforceP99Latency bool
MaxP99Latency time.Duration
}
// AcceptanceResult is a machine-readable evaluation of one bounded workload.
// It carries no target, proxy, client, credential, or request identifier data.
type AcceptanceResult struct {
Passed bool `json:"passed"`
FailureRate float64 `json:"failureRate"`
Violations []string `json:"violations,omitempty"`
}
// EvaluateAcceptance applies the requested thresholds to a completed report.
// A report without completed requests cannot prove any requested threshold.
func EvaluateAcceptance(report Report, criteria AcceptanceCriteria) (AcceptanceResult, error) {
if criteria.EnforceFailureRate && (math.IsNaN(criteria.MaxFailureRate) || math.IsInf(criteria.MaxFailureRate, 0) ||
criteria.MaxFailureRate < 0 || criteria.MaxFailureRate > 1) {
return AcceptanceResult{}, ErrInvalidAcceptanceCriteria
}
if criteria.EnforceP99Latency && criteria.MaxP99Latency <= 0 {
return AcceptanceResult{}, ErrInvalidAcceptanceCriteria
}
result := AcceptanceResult{Passed: true}
if !criteria.EnforceFailureRate && !criteria.EnforceP99Latency {
return result, nil
}
if report.Completed == 0 {
result.Passed = false
result.Violations = []string{"no completed requests were measured"}
return result, nil
}
result.FailureRate = float64(report.Failed) / float64(report.Completed)
if criteria.EnforceFailureRate && result.FailureRate > criteria.MaxFailureRate {
result.Passed = false
result.Violations = append(result.Violations,
fmt.Sprintf("failure rate %.6f exceeded limit %.6f", result.FailureRate, criteria.MaxFailureRate))
}
if criteria.EnforceP99Latency {
if report.Latency.Samples == 0 {
result.Passed = false
result.Violations = append(result.Violations, "no latency samples were measured")
} else if report.Latency.P99UpperBound > criteria.MaxP99Latency {
result.Passed = false
result.Violations = append(result.Violations,
fmt.Sprintf("p99 latency %s exceeded limit %s", report.Latency.P99UpperBound, criteria.MaxP99Latency))
}
}
return result, nil
}