mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
89 lines
2.9 KiB
Go
89 lines
2.9 KiB
Go
//go:build linux
|
|
|
|
package scenario
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/nezhahq/nezha/integration/agentcompat/internal/contract"
|
|
)
|
|
|
|
var ErrStressSoakTrend = errors.New("stress soak RSS increases strictly across all iterations")
|
|
|
|
type StressRSSSeries struct {
|
|
Process StressProcessIdentity `json:"process"`
|
|
EndRSSBytes [3]uint64 `json:"end_rss_bytes"`
|
|
}
|
|
|
|
type StressSoakTrendEvidence struct {
|
|
Series []StressRSSSeries `json:"series"`
|
|
}
|
|
|
|
func ValidateStressSoakTrend(evidence StressSoakTrendEvidence) error {
|
|
if len(evidence.Series) == 0 {
|
|
return errors.New("stress soak RSS series are empty")
|
|
}
|
|
seen := make(map[string]struct{}, len(evidence.Series))
|
|
for _, series := range evidence.Series {
|
|
key := series.Process.key()
|
|
if series.Process.PID < 1 || key == ":0" || (series.Process.Kind == StressProcessAgent && series.Process.Agent.Int() < 1) {
|
|
return fmt.Errorf("process=%s: %w", key, ErrStressIdentity)
|
|
}
|
|
if _, duplicate := seen[key]; duplicate {
|
|
return fmt.Errorf("duplicate process=%s: %w", key, ErrStressIdentity)
|
|
}
|
|
seen[key] = struct{}{}
|
|
values := series.EndRSSBytes
|
|
if values[0] < values[1] && values[1] < values[2] {
|
|
return fmt.Errorf("process=%s RSS=%v: %w", key, values, ErrStressSoakTrend)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ValidateStressSoakTrendForProfile(profile contract.Profile, evidence StressSoakTrendEvidence) error {
|
|
want := profile.AgentCount() + 1
|
|
if len(evidence.Series) != want {
|
|
return fmt.Errorf("soak series=%d want=%d: %w", len(evidence.Series), want, ErrStressIdentity)
|
|
}
|
|
seenDashboard := 0
|
|
seenAgents := make(map[int]struct{}, profile.AgentCount())
|
|
for _, series := range evidence.Series {
|
|
if err := validateStressSoakSeries(series); err != nil {
|
|
return err
|
|
}
|
|
switch series.Process.Kind {
|
|
case StressProcessDashboard:
|
|
seenDashboard++
|
|
case StressProcessAgent:
|
|
ordinal := series.Process.Agent.Int()
|
|
if ordinal < 1 || ordinal > profile.AgentCount() {
|
|
return fmt.Errorf("unknown agent ordinal=%d: %w", ordinal, ErrStressIdentity)
|
|
}
|
|
if _, duplicate := seenAgents[ordinal]; duplicate {
|
|
return fmt.Errorf("duplicate agent ordinal=%d: %w", ordinal, ErrStressIdentity)
|
|
}
|
|
seenAgents[ordinal] = struct{}{}
|
|
default:
|
|
return fmt.Errorf("unknown process kind=%q: %w", series.Process.Kind, ErrStressIdentity)
|
|
}
|
|
}
|
|
if seenDashboard != 1 || len(seenAgents) != profile.AgentCount() {
|
|
return fmt.Errorf("dashboard=%d agents=%d: %w", seenDashboard, len(seenAgents), ErrStressIdentity)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateStressSoakSeries(series StressRSSSeries) error {
|
|
key := series.Process.key()
|
|
if series.Process.PID < 1 || key == ":0" || (series.Process.Kind == StressProcessAgent && series.Process.Agent.Int() < 1) {
|
|
return fmt.Errorf("process=%s: %w", key, ErrStressIdentity)
|
|
}
|
|
values := series.EndRSSBytes
|
|
if values[0] < values[1] && values[1] < values[2] {
|
|
return fmt.Errorf("process=%s RSS=%v: %w", key, values, ErrStressSoakTrend)
|
|
}
|
|
return nil
|
|
}
|