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 }