github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/backoff/backoff_test.go (about)

     1  package backoff
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestDelays(t *testing.T) {
    13  	duration := func(s string) (d time.Duration) {
    14  		d, err := time.ParseDuration(s)
    15  		if err != nil {
    16  			panic(err)
    17  		}
    18  
    19  		return d
    20  	}
    21  	b := New(
    22  		WithSlotDuration(duration("500ms")),
    23  		WithCeiling(6),
    24  		WithJitterLimit(1),
    25  		WithSeed(0),
    26  	)
    27  	for i, d := range map[int]time.Duration{
    28  		0: duration("500ms"),
    29  		1: duration("1s"),
    30  		2: duration("2s"),
    31  		3: duration("4s"),
    32  		4: duration("8s"),
    33  		5: duration("16s"),
    34  		6: duration("32s"),
    35  		7: duration("32s"),
    36  		8: duration("32s"),
    37  	} {
    38  		t.Run(fmt.Sprintf("%v -> %v", i, d), func(t *testing.T) {
    39  			require.Equal(t, d, b.Delay(i))
    40  		})
    41  	}
    42  }
    43  
    44  func TestLogBackoff(t *testing.T) {
    45  	type exp struct {
    46  		eq  time.Duration
    47  		gte time.Duration
    48  		lte time.Duration
    49  	}
    50  	for _, tt := range []struct {
    51  		backoff Backoff
    52  		exp     []exp
    53  		seeds   int64
    54  	}{
    55  		{
    56  			backoff: New(
    57  				WithSlotDuration(time.Second),
    58  				WithCeiling(3),
    59  				WithJitterLimit(0),
    60  				WithSeed(0),
    61  			),
    62  			exp: []exp{
    63  				{gte: 0, lte: time.Second},
    64  				{gte: 0, lte: 2 * time.Second},
    65  				{gte: 0, lte: 4 * time.Second},
    66  				{gte: 0, lte: 8 * time.Second},
    67  				{gte: 0, lte: 8 * time.Second},
    68  				{gte: 0, lte: 8 * time.Second},
    69  				{gte: 0, lte: 8 * time.Second},
    70  			},
    71  			seeds: 1000,
    72  		},
    73  		{
    74  			backoff: New(
    75  				WithSlotDuration(time.Second),
    76  				WithCeiling(3),
    77  				WithJitterLimit(0.5),
    78  				WithSeed(0),
    79  			),
    80  			exp: []exp{
    81  				{gte: 500 * time.Millisecond, lte: time.Second},
    82  				{gte: 1 * time.Second, lte: 2 * time.Second},
    83  				{gte: 2 * time.Second, lte: 4 * time.Second},
    84  				{gte: 4 * time.Second, lte: 8 * time.Second},
    85  				{gte: 4 * time.Second, lte: 8 * time.Second},
    86  				{gte: 4 * time.Second, lte: 8 * time.Second},
    87  				{gte: 4 * time.Second, lte: 8 * time.Second},
    88  			},
    89  			seeds: 1000,
    90  		},
    91  		{
    92  			backoff: New(
    93  				WithSlotDuration(time.Second),
    94  				WithCeiling(3),
    95  				WithJitterLimit(1),
    96  				WithSeed(0),
    97  			),
    98  			exp: []exp{
    99  				{eq: time.Second},
   100  				{eq: 2 * time.Second},
   101  				{eq: 4 * time.Second},
   102  				{eq: 8 * time.Second},
   103  				{eq: 8 * time.Second},
   104  				{eq: 8 * time.Second},
   105  				{eq: 8 * time.Second},
   106  			},
   107  		},
   108  		{
   109  			backoff: New(
   110  				WithSlotDuration(time.Second),
   111  				WithCeiling(6),
   112  				WithJitterLimit(1),
   113  				WithSeed(0),
   114  			),
   115  			exp: []exp{
   116  				{eq: time.Second},
   117  				{eq: 2 * time.Second},
   118  				{eq: 4 * time.Second},
   119  				{eq: 8 * time.Second},
   120  				{eq: 16 * time.Second},
   121  				{eq: 32 * time.Second},
   122  				{eq: 64 * time.Second},
   123  				{eq: 64 * time.Second},
   124  				{eq: 64 * time.Second},
   125  				{eq: 64 * time.Second},
   126  				{eq: 64 * time.Second},
   127  				{eq: 64 * time.Second},
   128  			},
   129  		},
   130  	} {
   131  		t.Run("", func(t *testing.T) {
   132  			if tt.seeds == 0 {
   133  				tt.seeds = 1
   134  			}
   135  			for seed := int64(0); seed < tt.seeds; seed++ {
   136  				for n, exp := range tt.exp {
   137  					act := tt.backoff.Delay(n)
   138  					if eq := exp.eq; eq != 0 {
   139  						if eq != act {
   140  							t.Fatalf(
   141  								"unexpected Backoff delay: %s; want %s",
   142  								act, eq,
   143  							)
   144  						}
   145  
   146  						continue
   147  					}
   148  					if gte := exp.gte; act <= gte {
   149  						t.Errorf(
   150  							"unexpected Backoff delay: %s; want >= %s",
   151  							act, gte,
   152  						)
   153  					}
   154  					if lte := exp.lte; act >= lte {
   155  						t.Errorf(
   156  							"unexpected Backoff delay: %s; want <= %s",
   157  							act, lte,
   158  						)
   159  					}
   160  				}
   161  			}
   162  		})
   163  	}
   164  }
   165  
   166  func TestFastSlowDelaysWithoutJitter(t *testing.T) {
   167  	for _, tt := range []struct {
   168  		name    string
   169  		backoff Backoff
   170  		exp     []time.Duration
   171  	}{
   172  		{
   173  			name: "FastBackoff",
   174  			backoff: func() (backoff logBackoff) {
   175  				backoff = Fast
   176  				backoff.jitterLimit = 1
   177  
   178  				return backoff
   179  			}(),
   180  			exp: []time.Duration{
   181  				5 * time.Millisecond,
   182  				10 * time.Millisecond,
   183  				20 * time.Millisecond,
   184  				40 * time.Millisecond,
   185  				80 * time.Millisecond,
   186  				160 * time.Millisecond,
   187  				320 * time.Millisecond,
   188  				320 * time.Millisecond,
   189  				320 * time.Millisecond,
   190  				320 * time.Millisecond,
   191  				320 * time.Millisecond,
   192  			},
   193  		},
   194  		{
   195  			name: "SlowBackoff",
   196  			backoff: func() (backoff logBackoff) {
   197  				backoff = Slow
   198  				backoff.jitterLimit = 1
   199  
   200  				return backoff
   201  			}(),
   202  			exp: []time.Duration{
   203  				time.Second,
   204  				2 * time.Second,
   205  				4 * time.Second,
   206  				8 * time.Second,
   207  				16 * time.Second,
   208  				32 * time.Second,
   209  				64 * time.Second,
   210  				64 * time.Second,
   211  				64 * time.Second,
   212  				64 * time.Second,
   213  				64 * time.Second,
   214  			},
   215  		},
   216  	} {
   217  		t.Run(tt.name, func(t *testing.T) {
   218  			for n, exp := range tt.exp {
   219  				t.Run("delay#"+strconv.Itoa(n), func(t *testing.T) {
   220  					act := tt.backoff.Delay(n)
   221  					require.Equal(t, exp, act)
   222  				})
   223  			}
   224  		})
   225  	}
   226  }