Add ‘hook’ package for retryable functions
The hook package enables static and backoff retryable functions. This package can be used for calling remote webhooks concurrently in separate goroutines.
This commit is contained in:
parent
cfa6452863
commit
44591abfe5
1 changed files with 111 additions and 0 deletions
111
util/hook/hook.go
Normal file
111
util/hook/hook.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
// 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)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue