surrealpatch/util/hook/hook.go

112 lines
2.3 KiB
Go
Raw Normal View History

// 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)
}
}