68 lines
2.4 KiB
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
|
|
}
|