112 lines
2.3 KiB
Go
112 lines
2.3 KiB
Go
|
// Copyright © 2016 Abcum Ltd
|
||
|
//
|
||
|
// 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 hook
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
Static Kind = iota
|
||
|
Backoff
|
||
|
)
|
||
|
|
||
|
// Kind specifies the policy type.
|
||
|
type Kind int
|
||
|
|
||
|
// Policy represents a retryable function policy.
|
||
|
type Policy struct {
|
||
|
// Type is the policy type
|
||
|
Type Kind
|
||
|
// Attempts to retry
|
||
|
Retry int
|
||
|
// Factor is the backoff rate
|
||
|
Factor int
|
||
|
// Sleep is the initial duration to wait before retrying
|
||
|
Sleep time.Duration
|
||
|
}
|
||
|
|
||
|
// New creates a new static retryable policy, which retries
|
||
|
// after the duration of 'sleep', until the number of retries
|
||
|
// has been reached.
|
||
|
func NewStatic(retry int, sleep time.Duration) *Policy {
|
||
|
return &Policy{
|
||
|
Type: Static,
|
||
|
Retry: retry,
|
||
|
Sleep: sleep,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// New creates a new backoff retryable policy, which increases
|
||
|
// the delay between subsequent retries by the secified factor,
|
||
|
// until the number of retries has been reached.
|
||
|
func NewBackoff(retry, factor int, sleep time.Duration) *Policy {
|
||
|
return &Policy{
|
||
|
Type: Backoff,
|
||
|
Retry: retry,
|
||
|
Sleep: sleep,
|
||
|
Factor: factor,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Run executes a function until:
|
||
|
// 1. A nil error is returned,
|
||
|
// 2. The max number of retries has been reached,
|
||
|
// 3. The specified context has been cancelled or timedout.
|
||
|
func (p *Policy) Run(ctx context.Context, fnc func() error) error {
|
||
|
|
||
|
c := make(chan error, 1)
|
||
|
|
||
|
go func() { c <- p.run(ctx, fnc) }()
|
||
|
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
return ctx.Err()
|
||
|
case err := <-c:
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func (p *Policy) run(ctx context.Context, fnc func() error) error {
|
||
|
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
return ctx.Err()
|
||
|
default:
|
||
|
if err := fnc(); err != nil {
|
||
|
if p.Retry > 0 {
|
||
|
p.sleep()
|
||
|
p.Retry = p.Retry - 1
|
||
|
return p.run(ctx, fnc)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
|
||
|
}
|
||
|
|
||
|
func (p *Policy) sleep() {
|
||
|
|
||
|
time.Sleep(p.Sleep)
|
||
|
|
||
|
if p.Type == Backoff {
|
||
|
p.Sleep = p.Sleep * time.Duration(p.Factor)
|
||
|
}
|
||
|
|
||
|
}
|