mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e1c2276a5 | ||
|
|
d367397dab |
@@ -1,6 +1,7 @@
|
||||
package compare
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -11,6 +12,7 @@ func TestEqual(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestEqual")
|
||||
|
||||
// basic type
|
||||
assert.Equal(true, Equal(1, 1))
|
||||
assert.Equal(true, Equal(int64(1), int64(1)))
|
||||
assert.Equal(true, Equal("a", "a"))
|
||||
@@ -25,6 +27,7 @@ func TestEqual(t *testing.T) {
|
||||
assert.Equal(false, Equal([]int{1, 2}, []int{1, 2, 3}))
|
||||
assert.Equal(false, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}))
|
||||
|
||||
// time
|
||||
time1 := time.Now()
|
||||
time2 := time1.Add(time.Second)
|
||||
time3 := time1.Add(time.Second)
|
||||
@@ -32,6 +35,7 @@ func TestEqual(t *testing.T) {
|
||||
assert.Equal(false, Equal(time1, time2))
|
||||
assert.Equal(true, Equal(time2, time3))
|
||||
|
||||
// struct
|
||||
st1 := struct {
|
||||
A string
|
||||
B string
|
||||
@@ -58,6 +62,19 @@ func TestEqual(t *testing.T) {
|
||||
|
||||
assert.Equal(true, Equal(st1, st2))
|
||||
assert.Equal(false, Equal(st1, st3))
|
||||
|
||||
//byte slice
|
||||
bs1 := []byte("hello")
|
||||
bs2 := []byte("hello")
|
||||
assert.Equal(true, Equal(bs1, bs2))
|
||||
|
||||
// json.Number
|
||||
var jsonNumber1, jsonNumber2 json.Number
|
||||
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||
json.Unmarshal([]byte(`123`), &jsonNumber2)
|
||||
|
||||
assert.Equal(true, Equal(jsonNumber1, jsonNumber2))
|
||||
|
||||
}
|
||||
|
||||
func TestEqualValue(t *testing.T) {
|
||||
@@ -69,6 +86,13 @@ func TestEqualValue(t *testing.T) {
|
||||
assert.Equal(true, EqualValue(1, "1"))
|
||||
|
||||
assert.Equal(false, EqualValue(1, "2"))
|
||||
|
||||
// json.Number
|
||||
var jsonNumber1, jsonNumber2 json.Number
|
||||
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||
json.Unmarshal([]byte(`123`), &jsonNumber2)
|
||||
|
||||
assert.Equal(true, EqualValue(jsonNumber1, 123))
|
||||
}
|
||||
|
||||
func TestLessThan(t *testing.T) {
|
||||
@@ -85,6 +109,17 @@ func TestLessThan(t *testing.T) {
|
||||
|
||||
assert.Equal(false, LessThan(1, 1))
|
||||
assert.Equal(false, LessThan(1, int64(1)))
|
||||
|
||||
bs1 := []byte("hello1")
|
||||
bs2 := []byte("hello2")
|
||||
assert.Equal(true, LessThan(bs1, bs2))
|
||||
|
||||
// json.Number
|
||||
var jsonNumber1, jsonNumber2 json.Number
|
||||
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||
json.Unmarshal([]byte(`124`), &jsonNumber2)
|
||||
|
||||
assert.Equal(true, LessThan(jsonNumber1, jsonNumber2))
|
||||
}
|
||||
|
||||
func TestGreaterThan(t *testing.T) {
|
||||
@@ -102,6 +137,17 @@ func TestGreaterThan(t *testing.T) {
|
||||
assert.Equal(false, GreaterThan(1, 2))
|
||||
assert.Equal(false, GreaterThan(int64(2), 1))
|
||||
assert.Equal(false, GreaterThan("b", "c"))
|
||||
|
||||
bs1 := []byte("hello1")
|
||||
bs2 := []byte("hello2")
|
||||
assert.Equal(true, GreaterThan(bs2, bs1))
|
||||
|
||||
// json.Number
|
||||
var jsonNumber1, jsonNumber2 json.Number
|
||||
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||
json.Unmarshal([]byte(`124`), &jsonNumber2)
|
||||
|
||||
assert.Equal(true, GreaterThan(jsonNumber2, jsonNumber1))
|
||||
}
|
||||
|
||||
func TestLessOrEqual(t *testing.T) {
|
||||
@@ -119,6 +165,17 @@ func TestLessOrEqual(t *testing.T) {
|
||||
|
||||
assert.Equal(false, LessOrEqual(2, 1))
|
||||
assert.Equal(false, LessOrEqual(1, int64(2)))
|
||||
|
||||
bs1 := []byte("hello1")
|
||||
bs2 := []byte("hello2")
|
||||
assert.Equal(true, LessOrEqual(bs1, bs2))
|
||||
|
||||
// json.Number
|
||||
var jsonNumber1, jsonNumber2 json.Number
|
||||
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||
json.Unmarshal([]byte(`124`), &jsonNumber2)
|
||||
|
||||
assert.Equal(true, LessOrEqual(jsonNumber1, jsonNumber2))
|
||||
}
|
||||
|
||||
func TestGreaterOrEqual(t *testing.T) {
|
||||
@@ -137,6 +194,17 @@ func TestGreaterOrEqual(t *testing.T) {
|
||||
assert.Equal(false, GreaterOrEqual(1, 2))
|
||||
assert.Equal(false, GreaterOrEqual(int64(2), 1))
|
||||
assert.Equal(false, GreaterOrEqual("b", "c"))
|
||||
|
||||
bs1 := []byte("hello1")
|
||||
bs2 := []byte("hello2")
|
||||
assert.Equal(true, GreaterOrEqual(bs2, bs1))
|
||||
|
||||
// json.Number
|
||||
var jsonNumber1, jsonNumber2 json.Number
|
||||
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||
json.Unmarshal([]byte(`124`), &jsonNumber2)
|
||||
|
||||
assert.Equal(true, GreaterOrEqual(jsonNumber2, jsonNumber1))
|
||||
}
|
||||
|
||||
func TestInDelta(t *testing.T) {
|
||||
|
||||
@@ -6,10 +6,11 @@ import (
|
||||
|
||||
func TestAssert(t *testing.T) {
|
||||
assert := NewAssert(t, "TestAssert")
|
||||
|
||||
assert.Equal(0, 0)
|
||||
assert.NotEqual(1, 0)
|
||||
|
||||
assert.NotEqual("1", 1)
|
||||
|
||||
var uInt1 uint
|
||||
var uInt2 uint
|
||||
var uInt8 uint8
|
||||
@@ -47,4 +48,11 @@ func TestAssert(t *testing.T) {
|
||||
assert.IsNil(nil)
|
||||
assert.IsNotNil("abc")
|
||||
|
||||
var valA int = 1
|
||||
var valB int64 = 1
|
||||
assert.NotEqual(valA, valB)
|
||||
assert.EqualValues(valA, valB)
|
||||
|
||||
assert.ShouldBeFalse(false)
|
||||
assert.ShouldBeTrue(true)
|
||||
}
|
||||
|
||||
+71
-2
@@ -8,6 +8,8 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -56,6 +58,36 @@ func RetryWithLinearBackoff(interval time.Duration) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// RetryWithExponentialWithJitterBackoff set exponential strategy backoff
|
||||
// todo: Add playground link
|
||||
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option {
|
||||
if interval <= 0 {
|
||||
panic("programming error: retry interval should not be lower or equal to 0")
|
||||
}
|
||||
|
||||
if maxJitter < 0 {
|
||||
panic("programming error: retry maxJitter should not be lower to 0")
|
||||
}
|
||||
|
||||
if base%2 == 0 {
|
||||
return func(rc *RetryConfig) {
|
||||
rc.backoffStrategy = &shiftExponentialWithJitter{
|
||||
interval: interval,
|
||||
maxJitter: maxJitter,
|
||||
shifter: uint64(math.Log2(float64(base))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return func(rc *RetryConfig) {
|
||||
rc.backoffStrategy = &exponentialWithJitter{
|
||||
interval: interval,
|
||||
base: time.Duration(base),
|
||||
maxJitter: maxJitter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Context set retry context config.
|
||||
// Play: https://go.dev/play/p/xnAOOXv9GkS
|
||||
func Context(ctx context.Context) Option {
|
||||
@@ -117,8 +149,45 @@ type linear struct {
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
// CalculateInterval is the method implementation for the linear struct.
|
||||
// It returns the fixed interval defined in the linear struct.
|
||||
// CalculateInterval calculates the next interval returns a constant.
|
||||
func (l *linear) CalculateInterval() time.Duration {
|
||||
return l.interval
|
||||
}
|
||||
|
||||
// exponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy.
|
||||
type exponentialWithJitter struct {
|
||||
base time.Duration // base is the multiplier for the exponential backoff.
|
||||
interval time.Duration // interval is the current backoff interval, which will be adjusted over time.
|
||||
maxJitter time.Duration // maxJitter is the maximum amount of jitter to apply to the backoff interval.
|
||||
}
|
||||
|
||||
// CalculateInterval calculates the next backoff interval with jitter and updates the interval.
|
||||
func (e *exponentialWithJitter) CalculateInterval() time.Duration {
|
||||
current := e.interval
|
||||
e.interval = e.interval * e.base
|
||||
return current + jitter(e.maxJitter)
|
||||
}
|
||||
|
||||
// shiftExponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy.
|
||||
type shiftExponentialWithJitter struct {
|
||||
interval time.Duration // interval is the current backoff interval, which will be adjusted over time.
|
||||
maxJitter time.Duration // maxJitter is the maximum amount of jitter to apply to the backoff interval.
|
||||
shifter uint64 // shift by n faster than multiplication
|
||||
}
|
||||
|
||||
// CalculateInterval calculates the next backoff interval with jitter and updates the interval.
|
||||
// Uses shift instead of multiplication
|
||||
func (e *shiftExponentialWithJitter) CalculateInterval() time.Duration {
|
||||
current := e.interval
|
||||
e.interval = e.interval << e.shifter
|
||||
return current + jitter(e.maxJitter)
|
||||
}
|
||||
|
||||
// Jitter adds a random duration, up to maxJitter,
|
||||
// to the current interval to introduce randomness and avoid synchronized patterns in retry behavior
|
||||
func jitter(maxJitter time.Duration) time.Duration {
|
||||
if maxJitter == 0 {
|
||||
return 0
|
||||
}
|
||||
return time.Duration(rand.Int63n(int64(maxJitter)) + 1)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,27 @@ func ExampleRetryWithLinearBackoff() {
|
||||
// 3
|
||||
}
|
||||
|
||||
func ExampleRetryWithExponentialWithJitterBackoff() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
|
||||
func ExampleRetryTimes() {
|
||||
number := 0
|
||||
|
||||
|
||||
@@ -26,6 +26,80 @@ func TestRetryFailed(t *testing.T) {
|
||||
assert.Equal(DefaultRetryTimes, number)
|
||||
}
|
||||
|
||||
func TestRetryShiftExponentialWithJitterFailed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetryShiftExponentialWithJitterFailed")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal(DefaultRetryTimes, number)
|
||||
}
|
||||
|
||||
func TestRetryExponentialWithJitterFailed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetryExponentialWithJitterFailed")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25))
|
||||
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal(DefaultRetryTimes, number)
|
||||
}
|
||||
|
||||
func TestRetryWithExponentialSucceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetryWithExponentialSucceeded")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == DefaultRetryTimes {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25))
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(DefaultRetryTimes, number)
|
||||
}
|
||||
|
||||
func TestRetryWithExponentialShiftSucceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetryWithExponentialShiftSucceeded")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == DefaultRetryTimes {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 4, time.Microsecond*25))
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(DefaultRetryTimes, number)
|
||||
}
|
||||
|
||||
func TestRetrySucceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -46,6 +120,74 @@ func TestRetrySucceeded(t *testing.T) {
|
||||
assert.Equal(DefaultRetryTimes, number)
|
||||
}
|
||||
|
||||
func TestRetryOneShotSucceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetryOneShotSucceeded")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(1, number)
|
||||
}
|
||||
|
||||
func TestRetryWithExponentialWithJitterBackoffShiftOneShotSucceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffShiftOneShotSucceeded")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(1, number)
|
||||
}
|
||||
|
||||
func TestRetryWithExponentialWithJitterBackoffOneShotSucceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffOneShotSucceeded")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25))
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(1, number)
|
||||
}
|
||||
|
||||
func TestRetryWithExponentialWithJitterBackoffNoJitterOneShotSucceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffNoJitterOneShotSucceeded")
|
||||
|
||||
var number int
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, 0))
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(1, number)
|
||||
}
|
||||
|
||||
func TestSetRetryTimes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user