mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-06 23:46:29 +02:00
86 lines
2.5 KiB
Go
86 lines
2.5 KiB
Go
// Package retry contains a simple retry mechanism defined by a slice of delay
|
|
// times. There are no maximum retries accounted for here. If retries should be
|
|
// limited, use a Timeout context to keep from retrying forever. This should
|
|
// probably be made into something more robust.
|
|
package retry
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
// queryPollIntervals is a slice of the delays before re-checking the status on
|
|
// an executing query, backing off from a short delay at first. This sequence
|
|
// has been selected with Athena queries in mind, which may operate very
|
|
// quickly for things like schema manipulation, or which may run for an
|
|
// extended period of time, when running an actual data analysis query.
|
|
// Long-running queries will exhaust their rapid retries quickly, and fall back
|
|
// to checking every few seconds or longer.
|
|
var DefaultPollIntervals = []time.Duration{
|
|
time.Millisecond,
|
|
2 * time.Millisecond,
|
|
2 * time.Millisecond,
|
|
5 * time.Millisecond,
|
|
10 * time.Millisecond,
|
|
20 * time.Millisecond,
|
|
50 * time.Millisecond,
|
|
50 * time.Millisecond,
|
|
100 * time.Millisecond,
|
|
100 * time.Millisecond,
|
|
200 * time.Millisecond,
|
|
500 * time.Millisecond,
|
|
time.Second,
|
|
2 * time.Second,
|
|
5 * time.Second,
|
|
10 * time.Second,
|
|
20 * time.Second,
|
|
30 * time.Second,
|
|
time.Minute,
|
|
}
|
|
|
|
// delayer keeps track of the current delay between retries.
|
|
type delayer struct {
|
|
Delays []time.Duration
|
|
currentIndex int
|
|
}
|
|
|
|
// Delay returns the current delay duration, and advances the index to the next
|
|
// delay defined. If the index has reached the end of the delay slice, then it
|
|
// will continue to return the maximum delay defined.
|
|
func (d *delayer) Delay() time.Duration {
|
|
t := d.Delays[d.currentIndex]
|
|
if d.currentIndex < len(d.Delays)-1 {
|
|
d.currentIndex++
|
|
}
|
|
return t
|
|
}
|
|
|
|
// Retry uses a slice of time.Duration interval delays to retry a function
|
|
// until it either errors or indicates that it is ready to proceed. If f
|
|
// returns true, or an error, the retry loop is broken. Pass a closure as f if
|
|
// you need to record a value from the operation that you are performing inside
|
|
// f.
|
|
func Retry(ctx context.Context, retryIntervals []time.Duration, f func() (bool, error)) (err error) {
|
|
if retryIntervals == nil || len(retryIntervals) == 0 {
|
|
retryIntervals = DefaultPollIntervals
|
|
}
|
|
|
|
d := delayer{Delays: retryIntervals}
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
default:
|
|
ok, err := f()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ok {
|
|
return nil
|
|
}
|
|
time.Sleep(d.Delay())
|
|
}
|
|
}
|
|
return err
|
|
}
|