summaryrefslogtreecommitdiff
path: root/vendor/github.com/cenkalti/backoff/v5
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cenkalti/backoff/v5')
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/.gitignore25
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/CHANGELOG.md29
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/LICENSE20
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/README.md31
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/backoff.go66
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/error.go46
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/exponential.go125
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/retry.go139
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/ticker.go83
-rw-r--r--vendor/github.com/cenkalti/backoff/v5/timer.go35
10 files changed, 599 insertions, 0 deletions
diff --git a/vendor/github.com/cenkalti/backoff/v5/.gitignore b/vendor/github.com/cenkalti/backoff/v5/.gitignore
new file mode 100644
index 000000000..50d95c548
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+# IDEs
+.idea/
diff --git a/vendor/github.com/cenkalti/backoff/v5/CHANGELOG.md b/vendor/github.com/cenkalti/backoff/v5/CHANGELOG.md
new file mode 100644
index 000000000..658c37436
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/CHANGELOG.md
@@ -0,0 +1,29 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [5.0.0] - 2024-12-19
+
+### Added
+
+- RetryAfterError can be returned from an operation to indicate how long to wait before the next retry.
+
+### Changed
+
+- Retry function now accepts additional options for specifying max number of tries and max elapsed time.
+- Retry function now accepts a context.Context.
+- Operation function signature changed to return result (any type) and error.
+
+### Removed
+
+- RetryNotify* and RetryWithData functions. Only single Retry function remains.
+- Optional arguments from ExponentialBackoff constructor.
+- Clock and Timer interfaces.
+
+### Fixed
+
+- The original error is returned from Retry if there's a PermanentError. (#144)
+- The Retry function respects the wrapped PermanentError. (#140)
diff --git a/vendor/github.com/cenkalti/backoff/v5/LICENSE b/vendor/github.com/cenkalti/backoff/v5/LICENSE
new file mode 100644
index 000000000..89b817996
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Cenk Altı
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/cenkalti/backoff/v5/README.md b/vendor/github.com/cenkalti/backoff/v5/README.md
new file mode 100644
index 000000000..4611b1d17
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/README.md
@@ -0,0 +1,31 @@
+# Exponential Backoff [![GoDoc][godoc image]][godoc]
+
+This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
+
+[Exponential backoff][exponential backoff wiki]
+is an algorithm that uses feedback to multiplicatively decrease the rate of some process,
+in order to gradually find an acceptable rate.
+The retries exponentially increase and stop increasing when a certain threshold is met.
+
+## Usage
+
+Import path is `github.com/cenkalti/backoff/v5`. Please note the version part at the end.
+
+For most cases, use `Retry` function. See [example_test.go][example] for an example.
+
+If you have specific needs, copy `Retry` function (from [retry.go][retry-src]) into your code and modify it as needed.
+
+## Contributing
+
+* I would like to keep this library as small as possible.
+* Please don't send a PR without opening an issue and discussing it first.
+* If proposed change is not a common use case, I will probably not accept it.
+
+[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v5
+[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
+
+[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java
+[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
+
+[retry-src]: https://github.com/cenkalti/backoff/blob/v5/retry.go
+[example]: https://github.com/cenkalti/backoff/blob/v5/example_test.go
diff --git a/vendor/github.com/cenkalti/backoff/v5/backoff.go b/vendor/github.com/cenkalti/backoff/v5/backoff.go
new file mode 100644
index 000000000..dd2b24ca7
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/backoff.go
@@ -0,0 +1,66 @@
+// Package backoff implements backoff algorithms for retrying operations.
+//
+// Use Retry function for retrying operations that may fail.
+// If Retry does not meet your needs,
+// copy/paste the function into your project and modify as you wish.
+//
+// There is also Ticker type similar to time.Ticker.
+// You can use it if you need to work with channels.
+//
+// See Examples section below for usage examples.
+package backoff
+
+import "time"
+
+// BackOff is a backoff policy for retrying an operation.
+type BackOff interface {
+ // NextBackOff returns the duration to wait before retrying the operation,
+ // backoff.Stop to indicate that no more retries should be made.
+ //
+ // Example usage:
+ //
+ // duration := backoff.NextBackOff()
+ // if duration == backoff.Stop {
+ // // Do not retry operation.
+ // } else {
+ // // Sleep for duration and retry operation.
+ // }
+ //
+ NextBackOff() time.Duration
+
+ // Reset to initial state.
+ Reset()
+}
+
+// Stop indicates that no more retries should be made for use in NextBackOff().
+const Stop time.Duration = -1
+
+// ZeroBackOff is a fixed backoff policy whose backoff time is always zero,
+// meaning that the operation is retried immediately without waiting, indefinitely.
+type ZeroBackOff struct{}
+
+func (b *ZeroBackOff) Reset() {}
+
+func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }
+
+// StopBackOff is a fixed backoff policy that always returns backoff.Stop for
+// NextBackOff(), meaning that the operation should never be retried.
+type StopBackOff struct{}
+
+func (b *StopBackOff) Reset() {}
+
+func (b *StopBackOff) NextBackOff() time.Duration { return Stop }
+
+// ConstantBackOff is a backoff policy that always returns the same backoff delay.
+// This is in contrast to an exponential backoff policy,
+// which returns a delay that grows longer as you call NextBackOff() over and over again.
+type ConstantBackOff struct {
+ Interval time.Duration
+}
+
+func (b *ConstantBackOff) Reset() {}
+func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }
+
+func NewConstantBackOff(d time.Duration) *ConstantBackOff {
+ return &ConstantBackOff{Interval: d}
+}
diff --git a/vendor/github.com/cenkalti/backoff/v5/error.go b/vendor/github.com/cenkalti/backoff/v5/error.go
new file mode 100644
index 000000000..beb2b38a2
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/error.go
@@ -0,0 +1,46 @@
+package backoff
+
+import (
+ "fmt"
+ "time"
+)
+
+// PermanentError signals that the operation should not be retried.
+type PermanentError struct {
+ Err error
+}
+
+// Permanent wraps the given err in a *PermanentError.
+func Permanent(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &PermanentError{
+ Err: err,
+ }
+}
+
+// Error returns a string representation of the Permanent error.
+func (e *PermanentError) Error() string {
+ return e.Err.Error()
+}
+
+// Unwrap returns the wrapped error.
+func (e *PermanentError) Unwrap() error {
+ return e.Err
+}
+
+// RetryAfterError signals that the operation should be retried after the given duration.
+type RetryAfterError struct {
+ Duration time.Duration
+}
+
+// RetryAfter returns a RetryAfter error that specifies how long to wait before retrying.
+func RetryAfter(seconds int) error {
+ return &RetryAfterError{Duration: time.Duration(seconds) * time.Second}
+}
+
+// Error returns a string representation of the RetryAfter error.
+func (e *RetryAfterError) Error() string {
+ return fmt.Sprintf("retry after %s", e.Duration)
+}
diff --git a/vendor/github.com/cenkalti/backoff/v5/exponential.go b/vendor/github.com/cenkalti/backoff/v5/exponential.go
new file mode 100644
index 000000000..c1f3e442d
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/exponential.go
@@ -0,0 +1,125 @@
+package backoff
+
+import (
+ "math/rand"
+ "time"
+)
+
+/*
+ExponentialBackOff is a backoff implementation that increases the backoff
+period for each retry attempt using a randomization function that grows exponentially.
+
+NextBackOff() is calculated using the following formula:
+
+ randomized interval =
+ RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
+
+In other words NextBackOff() will range between the randomization factor
+percentage below and above the retry interval.
+
+For example, given the following parameters:
+
+ RetryInterval = 2
+ RandomizationFactor = 0.5
+ Multiplier = 2
+
+the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
+multiplied by the exponential, that is, between 2 and 6 seconds.
+
+Note: MaxInterval caps the RetryInterval and not the randomized interval.
+
+If the time elapsed since an ExponentialBackOff instance is created goes past the
+MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
+
+The elapsed time can be reset by calling Reset().
+
+Example: Given the following default arguments, for 10 tries the sequence will be,
+and assuming we go over the MaxElapsedTime on the 10th try:
+
+ Request # RetryInterval (seconds) Randomized Interval (seconds)
+
+ 1 0.5 [0.25, 0.75]
+ 2 0.75 [0.375, 1.125]
+ 3 1.125 [0.562, 1.687]
+ 4 1.687 [0.8435, 2.53]
+ 5 2.53 [1.265, 3.795]
+ 6 3.795 [1.897, 5.692]
+ 7 5.692 [2.846, 8.538]
+ 8 8.538 [4.269, 12.807]
+ 9 12.807 [6.403, 19.210]
+ 10 19.210 backoff.Stop
+
+Note: Implementation is not thread-safe.
+*/
+type ExponentialBackOff struct {
+ InitialInterval time.Duration
+ RandomizationFactor float64
+ Multiplier float64
+ MaxInterval time.Duration
+
+ currentInterval time.Duration
+}
+
+// Default values for ExponentialBackOff.
+const (
+ DefaultInitialInterval = 500 * time.Millisecond
+ DefaultRandomizationFactor = 0.5
+ DefaultMultiplier = 1.5
+ DefaultMaxInterval = 60 * time.Second
+)
+
+// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
+func NewExponentialBackOff() *ExponentialBackOff {
+ return &ExponentialBackOff{
+ InitialInterval: DefaultInitialInterval,
+ RandomizationFactor: DefaultRandomizationFactor,
+ Multiplier: DefaultMultiplier,
+ MaxInterval: DefaultMaxInterval,
+ }
+}
+
+// Reset the interval back to the initial retry interval and restarts the timer.
+// Reset must be called before using b.
+func (b *ExponentialBackOff) Reset() {
+ b.currentInterval = b.InitialInterval
+}
+
+// NextBackOff calculates the next backoff interval using the formula:
+//
+// Randomized interval = RetryInterval * (1 ± RandomizationFactor)
+func (b *ExponentialBackOff) NextBackOff() time.Duration {
+ if b.currentInterval == 0 {
+ b.currentInterval = b.InitialInterval
+ }
+
+ next := getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
+ b.incrementCurrentInterval()
+ return next
+}
+
+// Increments the current interval by multiplying it with the multiplier.
+func (b *ExponentialBackOff) incrementCurrentInterval() {
+ // Check for overflow, if overflow is detected set the current interval to the max interval.
+ if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
+ b.currentInterval = b.MaxInterval
+ } else {
+ b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
+ }
+}
+
+// Returns a random value from the following interval:
+//
+// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval].
+func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
+ if randomizationFactor == 0 {
+ return currentInterval // make sure no randomness is used when randomizationFactor is 0.
+ }
+ var delta = randomizationFactor * float64(currentInterval)
+ var minInterval = float64(currentInterval) - delta
+ var maxInterval = float64(currentInterval) + delta
+
+ // Get a random value from the range [minInterval, maxInterval].
+ // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
+ // we want a 33% chance for selecting either 1, 2 or 3.
+ return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
+}
diff --git a/vendor/github.com/cenkalti/backoff/v5/retry.go b/vendor/github.com/cenkalti/backoff/v5/retry.go
new file mode 100644
index 000000000..e43f47fb8
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/retry.go
@@ -0,0 +1,139 @@
+package backoff
+
+import (
+ "context"
+ "errors"
+ "time"
+)
+
+// DefaultMaxElapsedTime sets a default limit for the total retry duration.
+const DefaultMaxElapsedTime = 15 * time.Minute
+
+// Operation is a function that attempts an operation and may be retried.
+type Operation[T any] func() (T, error)
+
+// Notify is a function called on operation error with the error and backoff duration.
+type Notify func(error, time.Duration)
+
+// retryOptions holds configuration settings for the retry mechanism.
+type retryOptions struct {
+ BackOff BackOff // Strategy for calculating backoff periods.
+ Timer timer // Timer to manage retry delays.
+ Notify Notify // Optional function to notify on each retry error.
+ MaxTries uint // Maximum number of retry attempts.
+ MaxElapsedTime time.Duration // Maximum total time for all retries.
+}
+
+type RetryOption func(*retryOptions)
+
+// WithBackOff configures a custom backoff strategy.
+func WithBackOff(b BackOff) RetryOption {
+ return func(args *retryOptions) {
+ args.BackOff = b
+ }
+}
+
+// withTimer sets a custom timer for managing delays between retries.
+func withTimer(t timer) RetryOption {
+ return func(args *retryOptions) {
+ args.Timer = t
+ }
+}
+
+// WithNotify sets a notification function to handle retry errors.
+func WithNotify(n Notify) RetryOption {
+ return func(args *retryOptions) {
+ args.Notify = n
+ }
+}
+
+// WithMaxTries limits the number of retry attempts.
+func WithMaxTries(n uint) RetryOption {
+ return func(args *retryOptions) {
+ args.MaxTries = n
+ }
+}
+
+// WithMaxElapsedTime limits the total duration for retry attempts.
+func WithMaxElapsedTime(d time.Duration) RetryOption {
+ return func(args *retryOptions) {
+ args.MaxElapsedTime = d
+ }
+}
+
+// Retry attempts the operation until success, a permanent error, or backoff completion.
+// It ensures the operation is executed at least once.
+//
+// Returns the operation result or error if retries are exhausted or context is cancelled.
+func Retry[T any](ctx context.Context, operation Operation[T], opts ...RetryOption) (T, error) {
+ // Initialize default retry options.
+ args := &retryOptions{
+ BackOff: NewExponentialBackOff(),
+ Timer: &defaultTimer{},
+ MaxElapsedTime: DefaultMaxElapsedTime,
+ }
+
+ // Apply user-provided options to the default settings.
+ for _, opt := range opts {
+ opt(args)
+ }
+
+ defer args.Timer.Stop()
+
+ startedAt := time.Now()
+ args.BackOff.Reset()
+ for numTries := uint(1); ; numTries++ {
+ // Execute the operation.
+ res, err := operation()
+ if err == nil {
+ return res, nil
+ }
+
+ // Stop retrying if maximum tries exceeded.
+ if args.MaxTries > 0 && numTries >= args.MaxTries {
+ return res, err
+ }
+
+ // Handle permanent errors without retrying.
+ var permanent *PermanentError
+ if errors.As(err, &permanent) {
+ return res, err
+ }
+
+ // Stop retrying if context is cancelled.
+ if cerr := context.Cause(ctx); cerr != nil {
+ return res, cerr
+ }
+
+ // Calculate next backoff duration.
+ next := args.BackOff.NextBackOff()
+ if next == Stop {
+ return res, err
+ }
+
+ // Reset backoff if RetryAfterError is encountered.
+ var retryAfter *RetryAfterError
+ if errors.As(err, &retryAfter) {
+ next = retryAfter.Duration
+ args.BackOff.Reset()
+ }
+
+ // Stop retrying if maximum elapsed time exceeded.
+ if args.MaxElapsedTime > 0 && time.Since(startedAt)+next > args.MaxElapsedTime {
+ return res, err
+ }
+
+ // Notify on error if a notifier function is provided.
+ if args.Notify != nil {
+ args.Notify(err, next)
+ }
+
+ // Wait for the next backoff period or context cancellation.
+ args.Timer.Start(next)
+ select {
+ case <-args.Timer.C():
+ case <-ctx.Done():
+ return res, context.Cause(ctx)
+ }
+ }
+}
diff --git a/vendor/github.com/cenkalti/backoff/v5/ticker.go b/vendor/github.com/cenkalti/backoff/v5/ticker.go
new file mode 100644
index 000000000..f0d4b2ae7
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/ticker.go
@@ -0,0 +1,83 @@
+package backoff
+
+import (
+ "sync"
+ "time"
+)
+
+// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
+//
+// Ticks will continue to arrive when the previous operation is still running,
+// so operations that take a while to fail could run in quick succession.
+type Ticker struct {
+ C <-chan time.Time
+ c chan time.Time
+ b BackOff
+ timer timer
+ stop chan struct{}
+ stopOnce sync.Once
+}
+
+// NewTicker returns a new Ticker containing a channel that will send
+// the time at times specified by the BackOff argument. Ticker is
+// guaranteed to tick at least once. The channel is closed when Stop
+// method is called or BackOff stops. It is not safe to manipulate the
+// provided backoff policy (notably calling NextBackOff or Reset)
+// while the ticker is running.
+func NewTicker(b BackOff) *Ticker {
+ c := make(chan time.Time)
+ t := &Ticker{
+ C: c,
+ c: c,
+ b: b,
+ timer: &defaultTimer{},
+ stop: make(chan struct{}),
+ }
+ t.b.Reset()
+ go t.run()
+ return t
+}
+
+// Stop turns off a ticker. After Stop, no more ticks will be sent.
+func (t *Ticker) Stop() {
+ t.stopOnce.Do(func() { close(t.stop) })
+}
+
+func (t *Ticker) run() {
+ c := t.c
+ defer close(c)
+
+ // Ticker is guaranteed to tick at least once.
+ afterC := t.send(time.Now())
+
+ for {
+ if afterC == nil {
+ return
+ }
+
+ select {
+ case tick := <-afterC:
+ afterC = t.send(tick)
+ case <-t.stop:
+ t.c = nil // Prevent future ticks from being sent to the channel.
+ return
+ }
+ }
+}
+
+func (t *Ticker) send(tick time.Time) <-chan time.Time {
+ select {
+ case t.c <- tick:
+ case <-t.stop:
+ return nil
+ }
+
+ next := t.b.NextBackOff()
+ if next == Stop {
+ t.Stop()
+ return nil
+ }
+
+ t.timer.Start(next)
+ return t.timer.C()
+}
diff --git a/vendor/github.com/cenkalti/backoff/v5/timer.go b/vendor/github.com/cenkalti/backoff/v5/timer.go
new file mode 100644
index 000000000..a89530974
--- /dev/null
+++ b/vendor/github.com/cenkalti/backoff/v5/timer.go
@@ -0,0 +1,35 @@
+package backoff
+
+import "time"
+
+type timer interface {
+ Start(duration time.Duration)
+ Stop()
+ C() <-chan time.Time
+}
+
+// defaultTimer implements Timer interface using time.Timer
+type defaultTimer struct {
+ timer *time.Timer
+}
+
+// C returns the timers channel which receives the current time when the timer fires.
+func (t *defaultTimer) C() <-chan time.Time {
+ return t.timer.C
+}
+
+// Start starts the timer to fire after the given duration
+func (t *defaultTimer) Start(duration time.Duration) {
+ if t.timer == nil {
+ t.timer = time.NewTimer(duration)
+ } else {
+ t.timer.Reset(duration)
+ }
+}
+
+// Stop is called when the timer is not used anymore and resources may be freed.
+func (t *defaultTimer) Stop() {
+ if t.timer != nil {
+ t.timer.Stop()
+ }
+}