VictoriaMetrics/app/vmctl/backoff/backoff_test.go
Dmytro Kozlov 6f401daacb
vmctl: add --backoff-retries, --backoff-factor, --backoff-min-duration global command-line flags (#6639)
### Describe Your Changes

Added `--vm-backoff-retries`, `--vm-backoff-factor`,
`--vm-backoff-min-duration` and `--vm-native-backoff-retries`,
`--vm-native-backoff-factor`, `--vm-native-backoff-min-duration`
command-line flags to the `vmctl` app. Those changes will help to
configure the retry backoff policy for different situations.

Related issue:
https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6622
### Checklist

The following checks are **mandatory**:

- [X] My change adheres [VictoriaMetrics contributing
guidelines](https://docs.victoriametrics.com/contributing/).

---------

Signed-off-by: hagen1778 <roman@victoriametrics.com>
Co-authored-by: hagen1778 <roman@victoriametrics.com>
2024-08-03 19:12:48 +02:00

143 lines
3.1 KiB
Go

package backoff
import (
"context"
"fmt"
"strings"
"testing"
"time"
)
func TestBackoffRetry_Failure(t *testing.T) {
f := func(backoffFactor float64, backoffRetries int, cancelTimeout time.Duration, retryFunc func() error, resultExpected int) {
t.Helper()
r := &Backoff{
retries: backoffRetries,
factor: backoffFactor,
minDuration: time.Millisecond * 10,
}
ctx := context.Background()
if cancelTimeout != 0 {
newCtx, cancelFn := context.WithTimeout(context.Background(), cancelTimeout)
ctx = newCtx
defer cancelFn()
}
result, err := r.Retry(ctx, retryFunc)
if err == nil {
t.Fatalf("expecting non-nil error")
}
if result != uint64(resultExpected) {
t.Fatalf("unexpected result: got %d; want %d", result, resultExpected)
}
}
// return bad request
retryFunc := func() error {
return ErrBadRequest
}
f(0, 0, 0, retryFunc, 0)
// empty retries values
retryFunc = func() error {
time.Sleep(time.Millisecond * 100)
return nil
}
f(0, 0, 0, retryFunc, 0)
// all retries failed test
backoffFactor := 0.1
backoffRetries := 5
cancelTimeout := time.Second * 0
retryFunc = func() error {
t := time.NewTicker(time.Millisecond * 5)
defer t.Stop()
for range t.C {
return fmt.Errorf("got some error")
}
return nil
}
resultExpected := 5
f(backoffFactor, backoffRetries, cancelTimeout, retryFunc, resultExpected)
// cancel context
backoffFactor = 1.7
backoffRetries = 5
cancelTimeout = time.Millisecond * 40
retryFunc = func() error {
return fmt.Errorf("got some error")
}
resultExpected = 3
f(backoffFactor, backoffRetries, cancelTimeout, retryFunc, resultExpected)
}
func TestBackoffRetry_Success(t *testing.T) {
f := func(retryFunc func() error, resultExpected int) {
t.Helper()
r := &Backoff{
retries: 5,
factor: 1.7,
minDuration: time.Millisecond * 10,
}
ctx := context.Background()
result, err := r.Retry(ctx, retryFunc)
if err != nil {
t.Fatalf("Retry() error: %s", err)
}
if result != uint64(resultExpected) {
t.Fatalf("unexpected result: got %d; want %d", result, resultExpected)
}
}
// only one retry test
counter := 0
retryFunc := func() error {
t := time.NewTicker(time.Millisecond * 5)
defer t.Stop()
for range t.C {
counter++
if counter%2 == 0 {
return fmt.Errorf("got some error")
}
if counter%3 == 0 {
return nil
}
}
return nil
}
resultExpected := 1
f(retryFunc, resultExpected)
}
func TestBackoff_New(t *testing.T) {
f := func(retries int, factor float64, minDuration time.Duration, errExpected string) {
t.Helper()
_, err := New(retries, factor, minDuration)
if err == nil {
if errExpected != "" {
t.Fatalf("expecting non-nil error")
}
return
}
if !strings.Contains(err.Error(), errExpected) {
t.Fatalf("unexpected error: got %q; want %q", err.Error(), errExpected)
}
}
// empty retries
f(0, 1.1, time.Millisecond*10, "retries must be greater than 0")
// empty factor
f(1, 0, time.Millisecond*10, "factor must be greater than 1")
// empty minDuration
f(1, 1.1, 0, "minimum duration must be greater than 0")
// no errors
f(1, 1.1, time.Millisecond*10, "")
}