mirror of https://github.com/milvus-io/milvus.git
Add new retry (#5911)
* Add new retry Signed-off-by: godchen <qingxiang.chen@zilliz.com> * fix gofmt Signed-off-by: godchen <qingxiang.chen@zilliz.com>pull/5919/head
parent
0317dbe7a3
commit
fbcd754e52
|
@ -0,0 +1,48 @@
|
|||
// Copyright (C) 2019-2020 Zilliz. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
// or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
package retry
|
||||
|
||||
import "time"
|
||||
|
||||
type Config struct {
|
||||
attempts uint
|
||||
sleep time.Duration
|
||||
maxSleepTime time.Duration
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
attempts: uint(10),
|
||||
sleep: 200 * time.Millisecond,
|
||||
maxSleepTime: 1 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
type Option func(*Config)
|
||||
|
||||
func Attempts(attempts uint) Option {
|
||||
return func(c *Config) {
|
||||
c.attempts = attempts
|
||||
}
|
||||
}
|
||||
|
||||
func Sleep(sleep time.Duration) Option {
|
||||
return func(c *Config) {
|
||||
c.sleep = sleep
|
||||
}
|
||||
}
|
||||
|
||||
func MaxSleepTime(maxSleepTime time.Duration) Option {
|
||||
return func(c *Config) {
|
||||
c.maxSleepTime = maxSleepTime
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (C) 2019-2020 Zilliz. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
// or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Do(ctx context.Context, fn func() error, opts ...Option) error {
|
||||
|
||||
c := NewDefaultConfig()
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
el := make(ErrorList, c.attempts)
|
||||
|
||||
for i := uint(0); i < c.attempts; i++ {
|
||||
if err := fn(); err != nil {
|
||||
if s, ok := err.(InterruptError); ok {
|
||||
return s.error
|
||||
}
|
||||
el[i] = err
|
||||
|
||||
select {
|
||||
case <-time.After(c.sleep):
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
c.sleep *= 2
|
||||
if c.sleep > c.maxSleepTime {
|
||||
c.sleep = c.maxSleepTime
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return el
|
||||
}
|
||||
|
||||
type ErrorList []error
|
||||
|
||||
func (el ErrorList) Error() string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("All attempts results:\n")
|
||||
for index, err := range el {
|
||||
builder.WriteString(fmt.Sprintf("attempt #%d:%s\n", index+1, err.Error()))
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
type InterruptError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func NoRetryError(err error) InterruptError {
|
||||
return InterruptError{err}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright (C) 2019-2020 Zilliz. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
// or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDo(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
n := 0
|
||||
testFn := func() error {
|
||||
if n < 3 {
|
||||
n++
|
||||
return fmt.Errorf("some error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err := Do(ctx, testFn)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestAttempts(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testFn := func() error {
|
||||
return fmt.Errorf("some error")
|
||||
}
|
||||
|
||||
err := Do(ctx, testFn, Attempts(1))
|
||||
assert.NotNil(t, err)
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
func TestMaxSleepTime(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testFn := func() error {
|
||||
return fmt.Errorf("some error")
|
||||
}
|
||||
|
||||
err := Do(ctx, testFn, Attempts(3), MaxSleepTime(200*time.Millisecond))
|
||||
assert.NotNil(t, err)
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
func TestSleep(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testFn := func() error {
|
||||
return fmt.Errorf("some error")
|
||||
}
|
||||
|
||||
err := Do(ctx, testFn, Attempts(3), Sleep(500*time.Millisecond))
|
||||
assert.NotNil(t, err)
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
func TestAllError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testFn := func() error {
|
||||
return fmt.Errorf("some error")
|
||||
}
|
||||
|
||||
err := Do(ctx, testFn, Attempts(3))
|
||||
assert.NotNil(t, err)
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
func TestContextDeadline(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
testFn := func() error {
|
||||
return fmt.Errorf("some error")
|
||||
}
|
||||
|
||||
err := Do(ctx, testFn)
|
||||
assert.NotNil(t, err)
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
func TestContextCancel(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
testFn := func() error {
|
||||
return fmt.Errorf("some error")
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
cancel()
|
||||
}()
|
||||
|
||||
err := Do(ctx, testFn)
|
||||
assert.NotNil(t, err)
|
||||
fmt.Println(err)
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (C) 2019-2020 Zilliz. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
// or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
package retry
|
||||
|
||||
import "time"
|
||||
|
||||
type Config struct {
|
||||
attempts uint
|
||||
sleep time.Duration
|
||||
maxSleepTime time.Duration
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
attempts: uint(10),
|
||||
sleep: 200 * time.Millisecond,
|
||||
maxSleepTime: 1 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
type Option func(*Config)
|
||||
|
||||
func Attempts(attempts uint) Option {
|
||||
return func(c *Config) {
|
||||
c.attempts = attempts
|
||||
}
|
||||
}
|
||||
|
||||
func Sleep(sleep time.Duration) Option {
|
||||
return func(c *Config) {
|
||||
c.sleep = sleep
|
||||
}
|
||||
}
|
||||
|
||||
func MaxSleepTime(maxSleepTime time.Duration) Option {
|
||||
return func(c *Config) {
|
||||
c.maxSleepTime = maxSleepTime
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue