diff --git a/cmd/tester/probe.go b/cmd/tester/probe.go index 0836b9a..e5f63b4 100644 --- a/cmd/tester/probe.go +++ b/cmd/tester/probe.go @@ -123,6 +123,17 @@ func newHTTPClient(opts probeOpts) *http.Client { // cancelled. Each completed probe posts a probeResultMsg into the // tea.Program via send. // +// The probe duration is subtracted from the sleep so the loop's +// *period* is the interval, not interval + probe time. If a 100ms +// interval probe takes 30ms, the next sleep is 70ms (minus jitter), +// not 100ms — otherwise a VIP with 30ms probes would actually fire +// every 130ms and the --interval flag would quietly lie. On the +// pathological case where a probe overruns the interval (slow VIP +// under load) we clamp the sleep to zero and fire the next probe +// immediately, but we never try to "catch up" by firing multiple +// back-to-back probes — that would flood an already-struggling +// backend right when it's slow. +// // The loop honors the global `paused` flag by simply skipping the // probe call while paused — the ticker keeps ticking so a resume // picks up at the next natural tick boundary instead of fast- @@ -141,6 +152,10 @@ func runProbeLoop(ctx context.Context, vip *vipInfo, opts probeOpts, send func(t if !paused.Load() { result := doProbe(ctx, vip, opts) send(result) + sleepFor -= result.Duration + if sleepFor < 0 { + sleepFor = 0 + } } select { case <-ctx.Done():