Skip to content

Commit

Permalink
Ported Cortex e2e modules, so we can reuse it smaller projects too. (#5)
Browse files Browse the repository at this point in the history
* Ported Cortex e2e modules, so we can reuse it smaller projects too.

Signed-off-by: Bartlomiej Plotka <[email protected]>

* Fix.

Signed-off-by: Bartlomiej Plotka <[email protected]>
  • Loading branch information
bwplotka authored Mar 26, 2021
1 parent 3d78f4d commit 425a09c
Show file tree
Hide file tree
Showing 21 changed files with 3,358 additions and 96 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ lint: $(FAILLINT) $(GOLANGCI_LINT) $(MISSPELL) build format docs check-git deps
$(call require_clean_work_tree,"detected not clean master before running lint - run make lint and commit changes.")
@echo ">> verifying imported "
for dir in $(MODULES) ; do \
cd $${dir} && $(FAILLINT) -paths "fmt.{Print,PrintfPrintln,Sprint}" -ignore-tests ./...; \
cd $${dir} && $(FAILLINT) -paths "fmt.{Print,PrintfPrintln,Sprint}" -ignore-tests ./... && \
$(FAILLINT) -paths "github.com/stretchr/testify=github.com/efficientgo/tools/core/pkg/testutil" ./...; \
done
@echo ">> examining all of the Go files"
for dir in $(MODULES) ; do \
Expand Down
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![core module docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/tools/core)
[![e2e module docs](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/efficientgo/tools/e2e)

Set of tools, packages and libraries that every open-source Go project always needs with almost no dependencies.
Set of lightweight tools, packages and modules that every open-source Go project always needs with almost no dependencies.

## Release model

Expand All @@ -14,7 +14,7 @@ API is considered stable, but rare API changes might occur. If they do - they wi

## Modules

### `github.com/efficientgo/tools/core`
### Module `github.com/efficientgo/tools/core`

The main module containing set of useful, core packages for testing, closing, running and repeating.

Expand Down Expand Up @@ -152,7 +152,25 @@ This module contains:
// Simplistic assertion helpers for testing code. TestOrBench utils for union of testing and benchmarks.
```

### `github.com/efficientgo/tools/copyright`
### Module `github.com/efficientgo/tools/e2e`

This module is a fully featured e2e suite allowing utilizing `go test` for setting hermetic up complex microservice testing scenarios using docker.

```go mdox-gen-exec="sh -c 'tail -n +6 e2e/doc.go'"
// This module is a fully featured e2e suite allowing utilizing `go test` for setting hermetic up complex microservice integration testing scenarios using docker.
// Example usages:
// * https://github.com/cortexproject/cortex/tree/master/integration
// * https://github.com/thanos-io/thanos/tree/master/test/e2e
//
// Check github.com/efficientgo/tools/e2e/db for common DBs services you can run out of the box.
```

Credits:

* [Cortex Team](https://github.com/cortexproject/cortex/tree/f639b1855c9f0c9564113709a6bce2996d151ec7/integration)
* Initial Author: [@pracucci](https://github.com/pracucci)

### Module `github.com/efficientgo/tools/copyright`

This module is a very simple CLI for ensuring copyright header on code files.

Expand Down
1 change: 0 additions & 1 deletion core/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/testify v1.6.1
go.uber.org/goleak v1.1.10
)
4 changes: 0 additions & 4 deletions core/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand All @@ -34,5 +32,3 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
112 changes: 112 additions & 0 deletions core/pkg/backoff/backoff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) The EfficientGo Authors.
// Licensed under the Apache License 2.0.

package backoff

// Copied from https://github.com/cortexproject/cortex/blob/0ec7b9664a01d538f1f49580b4c359a5c3cc755a/pkg/util/backoff.go

import (
"context"
"fmt"
"math/rand"
"time"
)

// Config configures a Backoff.
type Config struct {
Min time.Duration `yaml:"min_period"` // Start backoff at this level
Max time.Duration `yaml:"max_period"` // Increase exponentially to this level
MaxRetries int `yaml:"max_retries"` // Give up after this many; zero means infinite retries
}

// Backoff implements exponential backoff with randomized wait times.
type Backoff struct {
cfg Config
ctx context.Context
numRetries int
nextDelayMin time.Duration
nextDelayMax time.Duration
}

// New creates a Backoff object. Pass a Context that can also terminate the operation.
func New(ctx context.Context, cfg Config) *Backoff {
return &Backoff{
cfg: cfg,
ctx: ctx,
nextDelayMin: cfg.Min,
nextDelayMax: doubleDuration(cfg.Min, cfg.Max),
}
}

// Reset the Backoff back to its initial condition.
func (b *Backoff) Reset() {
b.numRetries = 0
b.nextDelayMin = b.cfg.Min
b.nextDelayMax = doubleDuration(b.cfg.Min, b.cfg.Max)
}

// Ongoing returns true if caller should keep going.
func (b *Backoff) Ongoing() bool {
// Stop if Context has errored or max retry count is exceeded.
return b.ctx.Err() == nil && (b.cfg.MaxRetries == 0 || b.numRetries < b.cfg.MaxRetries)
}

// Err returns the reason for terminating the backoff, or nil if it didn't terminate.
func (b *Backoff) Err() error {
if b.ctx.Err() != nil {
return b.ctx.Err()
}
if b.cfg.MaxRetries != 0 && b.numRetries >= b.cfg.MaxRetries {
return fmt.Errorf("terminated after %d retries", b.numRetries)
}
return nil
}

// NumRetries returns the number of retries so far.
func (b *Backoff) NumRetries() int {
return b.numRetries
}

// Wait sleeps for the backoff time then increases the retry count and backoff time.
// Returns immediately if Context is terminated.
func (b *Backoff) Wait() {
// Increase the number of retries and get the next delay.
sleepTime := b.NextDelay()

if b.Ongoing() {
select {
case <-b.ctx.Done():
case <-time.After(sleepTime):
}
}
}

func (b *Backoff) NextDelay() time.Duration {
b.numRetries++

// Handle the edge case the min and max have the same value
// (or due to some misconfig max is < min).
if b.nextDelayMin >= b.nextDelayMax {
return b.nextDelayMin
}

// Add a jitter within the next exponential backoff range.
sleepTime := b.nextDelayMin + time.Duration(rand.Int63n(int64(b.nextDelayMax-b.nextDelayMin)))

// Apply the exponential backoff to calculate the next jitter
// range, unless we've already reached the max.
if b.nextDelayMax < b.cfg.Max {
b.nextDelayMin = doubleDuration(b.nextDelayMin, b.cfg.Max)
b.nextDelayMax = doubleDuration(b.nextDelayMax, b.cfg.Max)
}

return sleepTime
}

func doubleDuration(value time.Duration, max time.Duration) time.Duration {
value = value * 2
if value <= max {
return value
}
return max
}
108 changes: 108 additions & 0 deletions core/pkg/backoff/backoff_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) The EfficientGo Authors.
// Licensed under the Apache License 2.0.

package backoff

// Copied from https://github.com/cortexproject/cortex/blob/0ec7b9664a01d538f1f49580b4c359a5c3cc755a/pkg/util/backoff.go

import (
"context"
"testing"
"time"
)

func TestBackoff_NextDelay(t *testing.T) {
t.Parallel()

tests := map[string]struct {
minBackoff time.Duration
maxBackoff time.Duration
expectedRanges [][]time.Duration
}{
"exponential backoff with jitter honoring min and max": {
minBackoff: 100 * time.Millisecond,
maxBackoff: 10 * time.Second,
expectedRanges: [][]time.Duration{
{100 * time.Millisecond, 200 * time.Millisecond},
{200 * time.Millisecond, 400 * time.Millisecond},
{400 * time.Millisecond, 800 * time.Millisecond},
{800 * time.Millisecond, 1600 * time.Millisecond},
{1600 * time.Millisecond, 3200 * time.Millisecond},
{3200 * time.Millisecond, 6400 * time.Millisecond},
{6400 * time.Millisecond, 10000 * time.Millisecond},
{6400 * time.Millisecond, 10000 * time.Millisecond},
},
},
"exponential backoff with max equal to the end of a range": {
minBackoff: 100 * time.Millisecond,
maxBackoff: 800 * time.Millisecond,
expectedRanges: [][]time.Duration{
{100 * time.Millisecond, 200 * time.Millisecond},
{200 * time.Millisecond, 400 * time.Millisecond},
{400 * time.Millisecond, 800 * time.Millisecond},
{400 * time.Millisecond, 800 * time.Millisecond},
},
},
"exponential backoff with max equal to the end of a range + 1": {
minBackoff: 100 * time.Millisecond,
maxBackoff: 801 * time.Millisecond,
expectedRanges: [][]time.Duration{
{100 * time.Millisecond, 200 * time.Millisecond},
{200 * time.Millisecond, 400 * time.Millisecond},
{400 * time.Millisecond, 800 * time.Millisecond},
{800 * time.Millisecond, 801 * time.Millisecond},
{800 * time.Millisecond, 801 * time.Millisecond},
},
},
"exponential backoff with max equal to the end of a range - 1": {
minBackoff: 100 * time.Millisecond,
maxBackoff: 799 * time.Millisecond,
expectedRanges: [][]time.Duration{
{100 * time.Millisecond, 200 * time.Millisecond},
{200 * time.Millisecond, 400 * time.Millisecond},
{400 * time.Millisecond, 799 * time.Millisecond},
{400 * time.Millisecond, 799 * time.Millisecond},
},
},
"min backoff is equal to max": {
minBackoff: 100 * time.Millisecond,
maxBackoff: 100 * time.Millisecond,
expectedRanges: [][]time.Duration{
{100 * time.Millisecond, 100 * time.Millisecond},
{100 * time.Millisecond, 100 * time.Millisecond},
{100 * time.Millisecond, 100 * time.Millisecond},
},
},
"min backoff is greater then max": {
minBackoff: 200 * time.Millisecond,
maxBackoff: 100 * time.Millisecond,
expectedRanges: [][]time.Duration{
{200 * time.Millisecond, 200 * time.Millisecond},
{200 * time.Millisecond, 200 * time.Millisecond},
{200 * time.Millisecond, 200 * time.Millisecond},
},
},
}

for testName, testData := range tests {
testData := testData

t.Run(testName, func(t *testing.T) {
t.Parallel()

b := New(context.Background(), Config{
Min: testData.minBackoff,
Max: testData.maxBackoff,
MaxRetries: len(testData.expectedRanges),
})

for _, expectedRange := range testData.expectedRanges {
delay := b.NextDelay()

if delay < expectedRange[0] || delay > expectedRange[1] {
t.Errorf("%d expected to be within %d and %d", delay, expectedRange[0], expectedRange[1])
}
}
})
}
}
Loading

0 comments on commit 425a09c

Please sign in to comment.