package api import ( "sync" "time" "greq/internal/storage" ) // StressCount is the number of requests fired in a single stress run. const StressCount = 100 // StressConcurrency is the number of parallel workers. const StressConcurrency = 10 // StressResult holds the aggregated statistics of a load test. type StressResult struct { Total int Errors int MinDur time.Duration MaxDur time.Duration AvgDur time.Duration } // ErrRate returns the error percentage (0–100). func (r StressResult) ErrRate() float64 { if r.Total == 0 { return 0 } return float64(r.Errors) / float64(r.Total) * 100 } // RunStress fires StressCount HTTP requests against rf with StressConcurrency // parallel workers and returns aggregated timing statistics. func RunStress(rf storage.RequestFile, envs map[string]string) StressResult { type singleResult struct { dur time.Duration err bool } results := make([]singleResult, StressCount) sem := make(chan struct{}, StressConcurrency) var mu sync.Mutex var wg sync.WaitGroup for i := 0; i < StressCount; i++ { wg.Add(1) sem <- struct{}{} go func(idx int) { defer wg.Done() defer func() { <-sem }() start := time.Now() _, err := Do(rf, envs) dur := time.Since(start) mu.Lock() results[idx] = singleResult{dur: dur, err: err != nil} mu.Unlock() }(i) } wg.Wait() var errCount int var sumDur, minDur, maxDur time.Duration for _, r := range results { if r.err { errCount++ } sumDur += r.dur if minDur == 0 || r.dur < minDur { minDur = r.dur } if r.dur > maxDur { maxDur = r.dur } } avgDur := time.Duration(int64(sumDur) / int64(StressCount)) return StressResult{ Total: StressCount, Errors: errCount, MinDur: minDur, MaxDur: maxDur, AvgDur: avgDur, } }