This repository has been archived by the owner on May 26, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
80f7185
commit e871419
Showing
14 changed files
with
479 additions
and
1,520 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,228 +1,67 @@ | ||
package discovery | ||
|
||
import ( | ||
"math" | ||
"math/rand" | ||
"sync" | ||
"time" | ||
|
||
dbackoff "github.com/libp2p/go-libp2p/p2p/discovery/backoff" | ||
) | ||
|
||
type BackoffFactory func() BackoffStrategy | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.BackoffFactory instead. | ||
type BackoffFactory = dbackoff.BackoffFactory | ||
|
||
// BackoffStrategy describes how backoff will be implemented. BackoffStratgies are stateful. | ||
type BackoffStrategy interface { | ||
// Delay calculates how long the next backoff duration should be, given the prior calls to Delay | ||
Delay() time.Duration | ||
// Reset clears the internal state of the BackoffStrategy | ||
Reset() | ||
} | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.BackoffStrategy instead. | ||
type BackoffStrategy = dbackoff.BackoffStrategy | ||
|
||
// Jitter implementations taken roughly from https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ | ||
|
||
// Jitter must return a duration between min and max. Min must be lower than, or equal to, max. | ||
type Jitter func(duration, min, max time.Duration, rng *rand.Rand) time.Duration | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.Jitter instead. | ||
type Jitter = dbackoff.Jitter | ||
|
||
// FullJitter returns a random number uniformly chose from the range [min, boundedDur]. | ||
// boundedDur is the duration bounded between min and max. | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.FullJitter instead. | ||
func FullJitter(duration, min, max time.Duration, rng *rand.Rand) time.Duration { | ||
if duration <= min { | ||
return min | ||
} | ||
|
||
normalizedDur := boundedDuration(duration, min, max) - min | ||
|
||
return boundedDuration(time.Duration(rng.Int63n(int64(normalizedDur)))+min, min, max) | ||
return dbackoff.FullJitter(duration, min, max, rng) | ||
} | ||
|
||
// NoJitter returns the duration bounded between min and max | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.NoJitter instead. | ||
func NoJitter(duration, min, max time.Duration, rng *rand.Rand) time.Duration { | ||
return boundedDuration(duration, min, max) | ||
} | ||
|
||
type randomizedBackoff struct { | ||
min time.Duration | ||
max time.Duration | ||
rng *rand.Rand | ||
} | ||
|
||
func (b *randomizedBackoff) BoundedDelay(duration time.Duration) time.Duration { | ||
return boundedDuration(duration, b.min, b.max) | ||
} | ||
|
||
func boundedDuration(d, min, max time.Duration) time.Duration { | ||
if d < min { | ||
return min | ||
} | ||
if d > max { | ||
return max | ||
} | ||
return d | ||
} | ||
|
||
type attemptBackoff struct { | ||
attempt int | ||
jitter Jitter | ||
randomizedBackoff | ||
} | ||
|
||
func (b *attemptBackoff) Reset() { | ||
b.attempt = 0 | ||
return dbackoff.NoJitter(duration, min, max, rng) | ||
} | ||
|
||
// NewFixedBackoff creates a BackoffFactory with a constant backoff duration | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.NewFixedBackoff instead. | ||
func NewFixedBackoff(delay time.Duration) BackoffFactory { | ||
return func() BackoffStrategy { | ||
return &fixedBackoff{delay: delay} | ||
} | ||
} | ||
|
||
type fixedBackoff struct { | ||
delay time.Duration | ||
} | ||
|
||
func (b *fixedBackoff) Delay() time.Duration { | ||
return b.delay | ||
return dbackoff.NewFixedBackoff(delay) | ||
} | ||
|
||
func (b *fixedBackoff) Reset() {} | ||
|
||
// NewPolynomialBackoff creates a BackoffFactory with backoff of the form c0*x^0, c1*x^1, ...cn*x^n where x is the attempt number | ||
// jitter is the function for adding randomness around the backoff | ||
// timeUnits are the units of time the polynomial is evaluated in | ||
// polyCoefs is the array of polynomial coefficients from [c0, c1, ... cn] | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.NewPolynomialBackoff instead. | ||
func NewPolynomialBackoff(min, max time.Duration, jitter Jitter, | ||
timeUnits time.Duration, polyCoefs []float64, rngSrc rand.Source) BackoffFactory { | ||
rng := rand.New(&lockedSource{src: rngSrc}) | ||
return func() BackoffStrategy { | ||
return &polynomialBackoff{ | ||
attemptBackoff: attemptBackoff{ | ||
randomizedBackoff: randomizedBackoff{ | ||
min: min, | ||
max: max, | ||
rng: rng, | ||
}, | ||
jitter: jitter, | ||
}, | ||
timeUnits: timeUnits, | ||
poly: polyCoefs, | ||
} | ||
} | ||
} | ||
|
||
type polynomialBackoff struct { | ||
attemptBackoff | ||
timeUnits time.Duration | ||
poly []float64 | ||
} | ||
|
||
func (b *polynomialBackoff) Delay() time.Duration { | ||
var polySum float64 | ||
switch len(b.poly) { | ||
case 0: | ||
return 0 | ||
case 1: | ||
polySum = b.poly[0] | ||
default: | ||
polySum = b.poly[0] | ||
exp := 1 | ||
attempt := b.attempt | ||
b.attempt++ | ||
|
||
for _, c := range b.poly[1:] { | ||
exp *= attempt | ||
polySum += float64(exp) * c | ||
} | ||
} | ||
return b.jitter(time.Duration(float64(b.timeUnits)*polySum), b.min, b.max, b.rng) | ||
return dbackoff.NewPolynomialBackoff(min, max, jitter, timeUnits, polyCoefs, rngSrc) | ||
} | ||
|
||
// NewExponentialBackoff creates a BackoffFactory with backoff of the form base^x + offset where x is the attempt number | ||
// jitter is the function for adding randomness around the backoff | ||
// timeUnits are the units of time the base^x is evaluated in | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.NewExponentialBackoff instead. | ||
func NewExponentialBackoff(min, max time.Duration, jitter Jitter, | ||
timeUnits time.Duration, base float64, offset time.Duration, rngSrc rand.Source) BackoffFactory { | ||
rng := rand.New(&lockedSource{src: rngSrc}) | ||
return func() BackoffStrategy { | ||
return &exponentialBackoff{ | ||
attemptBackoff: attemptBackoff{ | ||
randomizedBackoff: randomizedBackoff{ | ||
min: min, | ||
max: max, | ||
rng: rng, | ||
}, | ||
jitter: jitter, | ||
}, | ||
timeUnits: timeUnits, | ||
base: base, | ||
offset: offset, | ||
} | ||
} | ||
} | ||
|
||
type exponentialBackoff struct { | ||
attemptBackoff | ||
timeUnits time.Duration | ||
base float64 | ||
offset time.Duration | ||
} | ||
|
||
func (b *exponentialBackoff) Delay() time.Duration { | ||
attempt := b.attempt | ||
b.attempt++ | ||
return b.jitter( | ||
time.Duration(math.Pow(b.base, float64(attempt))*float64(b.timeUnits))+b.offset, b.min, b.max, b.rng) | ||
return dbackoff.NewExponentialBackoff(min, max, jitter, timeUnits, base, offset, rngSrc) | ||
} | ||
|
||
// NewExponentialDecorrelatedJitter creates a BackoffFactory with backoff of the roughly of the form base^x where x is the attempt number. | ||
// Delays start at the minimum duration and after each attempt delay = rand(min, delay * base), bounded by the max | ||
// See https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ for more information | ||
// Deprecated: use go-libp2p/p2p/discovery/backoff.NewExponentialDecorrelatedJitter instead. | ||
func NewExponentialDecorrelatedJitter(min, max time.Duration, base float64, rngSrc rand.Source) BackoffFactory { | ||
rng := rand.New(&lockedSource{src: rngSrc}) | ||
return func() BackoffStrategy { | ||
return &exponentialDecorrelatedJitter{ | ||
randomizedBackoff: randomizedBackoff{ | ||
min: min, | ||
max: max, | ||
rng: rng, | ||
}, | ||
base: base, | ||
} | ||
} | ||
} | ||
|
||
type exponentialDecorrelatedJitter struct { | ||
randomizedBackoff | ||
base float64 | ||
lastDelay time.Duration | ||
} | ||
|
||
func (b *exponentialDecorrelatedJitter) Delay() time.Duration { | ||
if b.lastDelay < b.min { | ||
b.lastDelay = b.min | ||
return b.lastDelay | ||
} | ||
|
||
nextMax := int64(float64(b.lastDelay) * b.base) | ||
b.lastDelay = boundedDuration(time.Duration(b.rng.Int63n(nextMax-int64(b.min)))+b.min, b.min, b.max) | ||
return b.lastDelay | ||
} | ||
|
||
func (b *exponentialDecorrelatedJitter) Reset() { b.lastDelay = 0 } | ||
|
||
type lockedSource struct { | ||
lk sync.Mutex | ||
src rand.Source | ||
} | ||
|
||
func (r *lockedSource) Int63() (n int64) { | ||
r.lk.Lock() | ||
n = r.src.Int63() | ||
r.lk.Unlock() | ||
return | ||
} | ||
|
||
func (r *lockedSource) Seed(seed int64) { | ||
r.lk.Lock() | ||
r.src.Seed(seed) | ||
r.lk.Unlock() | ||
return dbackoff.NewExponentialDecorrelatedJitter(min, max, base, rngSrc) | ||
} |
Oops, something went wrong.