Skip to content

Commit

Permalink
use backoff package instead of manual retry
Browse files Browse the repository at this point in the history
  • Loading branch information
Omarabdul3ziz committed Dec 7, 2023
1 parent 899b658 commit 2646bb8
Showing 1 changed file with 58 additions and 34 deletions.
92 changes: 58 additions & 34 deletions pkg/perf/iperf/iperf_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"path/filepath"
"time"

"github.com/cenkalti/backoff"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/threefoldtech/zos/pkg/environment"
Expand All @@ -20,8 +21,11 @@ import (

const (
maxRetries = 3
backoffInterval = 5 * time.Minute
errServerBusy = "the server is busy running a test. try again later"
initialInterval = 5 * time.Minute
maxInterval = 20 * time.Minute
maxElapsedTime = time.Duration(maxRetries) * maxInterval

errServerBusy = "the server is busy running a test. try again later"
)

// IperfTest for iperf tcp/udp tests
Expand Down Expand Up @@ -142,45 +146,65 @@ func (t *IperfTest) runIperfTest(ctx context.Context, clientIP string, tcp bool)
opts = append(opts, "--length", "16B", "--udp")
}

iperfResult := IperfResult{}
for round := 1; round <= maxRetries; round++ {
output, err := exec.CommandContext(ctx, "iperf", opts...).CombinedOutput()
exitErr := &exec.ExitError{}
if err != nil && !errors.As(err, &exitErr) {
log.Err(err).Msg("failed to run iperf")
return iperfResult
var report iperfCommandOutput
operation := func() error {
res := runIperfCommand(ctx, opts)
if res.Error == errServerBusy {
return fmt.Errorf(errServerBusy)
}

var report iperfCommandOutput
if err := json.Unmarshal(output, &report); err != nil {
log.Err(err).Msg("failed to parse iperf output")
return iperfResult
}
report = res
return nil
}

if report.Error == errServerBusy {
retryAfter := time.Duration(round) * backoffInterval
log.Err(err).Msgf("retrying again after %d min", retryAfter/time.Minute)
time.Sleep(retryAfter)
continue
}
notify := func(err error, waitTime time.Duration) {
log.Debug().Err(err).Stringer("retry-in", waitTime).Msg("retrying")
}

proto := "tcp"
if !tcp {
proto = "udp"
}
bo := backoff.NewExponentialBackOff()
bo.InitialInterval = initialInterval
bo.MaxInterval = maxInterval
bo.MaxElapsedTime = maxElapsedTime

iperfResult.UploadSpeed = report.End.SumSent.BitsPerSecond
iperfResult.DownloadSpeed = report.End.SumReceived.BitsPerSecond
iperfResult.CpuReport = report.End.CPUUtilizationPercent
iperfResult.NodeIpv4 = clientIP
iperfResult.TestType = proto
iperfResult.Error = report.Error
if !tcp && len(report.End.Streams) > 0 {
iperfResult.DownloadSpeed = report.End.Streams[0].UDP.BitsPerSecond
}
b := backoff.WithMaxRetries(bo, maxRetries)
err := backoff.RetryNotify(operation, b, notify)
if err != nil {
return IperfResult{}
}

break
proto := "tcp"
if !tcp {
proto = "udp"
}

iperfResult := IperfResult{
UploadSpeed: report.End.SumSent.BitsPerSecond,
DownloadSpeed: report.End.SumReceived.BitsPerSecond,
CpuReport: report.End.CPUUtilizationPercent,
NodeIpv4: clientIP,
TestType: proto,
Error: report.Error,
}
if !tcp && len(report.End.Streams) > 0 {
iperfResult.DownloadSpeed = report.End.Streams[0].UDP.BitsPerSecond
}

return iperfResult
}

func runIperfCommand(ctx context.Context, opts []string) iperfCommandOutput {
output, err := exec.CommandContext(ctx, "iperf", opts...).CombinedOutput()
exitErr := &exec.ExitError{}
if err != nil && !errors.As(err, &exitErr) {
log.Err(err).Msg("failed to run iperf")
return iperfCommandOutput{}
}

var report iperfCommandOutput
if err := json.Unmarshal(output, &report); err != nil {
log.Err(err).Msg("failed to parse iperf output")
return iperfCommandOutput{}
}

return report
}

0 comments on commit 2646bb8

Please sign in to comment.