|
|
|
@ -95,6 +95,26 @@ func (s *funcSub) Err() <-chan error { |
|
|
|
|
// Resubscribe applies backoff between calls to fn. The time between calls is adapted
|
|
|
|
|
// based on the error rate, but will never exceed backoffMax.
|
|
|
|
|
func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription { |
|
|
|
|
return ResubscribeErr(backoffMax, func(ctx context.Context, _ error) (Subscription, error) { |
|
|
|
|
return fn(ctx) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// A ResubscribeFunc attempts to establish a subscription.
|
|
|
|
|
type ResubscribeFunc func(context.Context) (Subscription, error) |
|
|
|
|
|
|
|
|
|
// ResubscribeErr calls fn repeatedly to keep a subscription established. When the
|
|
|
|
|
// subscription is established, ResubscribeErr waits for it to fail and calls fn again. This
|
|
|
|
|
// process repeats until Unsubscribe is called or the active subscription ends
|
|
|
|
|
// successfully.
|
|
|
|
|
//
|
|
|
|
|
// The difference between Resubscribe and ResubscribeErr is that with ResubscribeErr,
|
|
|
|
|
// the error of the failing subscription is available to the callback for logging
|
|
|
|
|
// purposes.
|
|
|
|
|
//
|
|
|
|
|
// ResubscribeErr applies backoff between calls to fn. The time between calls is adapted
|
|
|
|
|
// based on the error rate, but will never exceed backoffMax.
|
|
|
|
|
func ResubscribeErr(backoffMax time.Duration, fn ResubscribeErrFunc) Subscription { |
|
|
|
|
s := &resubscribeSub{ |
|
|
|
|
waitTime: backoffMax / 10, |
|
|
|
|
backoffMax: backoffMax, |
|
|
|
@ -106,15 +126,18 @@ func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription { |
|
|
|
|
return s |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// A ResubscribeFunc attempts to establish a subscription.
|
|
|
|
|
type ResubscribeFunc func(context.Context) (Subscription, error) |
|
|
|
|
// A ResubscribeErrFunc attempts to establish a subscription.
|
|
|
|
|
// For every call but the first, the second argument to this function is
|
|
|
|
|
// the error that occurred with the previous subscription.
|
|
|
|
|
type ResubscribeErrFunc func(context.Context, error) (Subscription, error) |
|
|
|
|
|
|
|
|
|
type resubscribeSub struct { |
|
|
|
|
fn ResubscribeFunc |
|
|
|
|
fn ResubscribeErrFunc |
|
|
|
|
err chan error |
|
|
|
|
unsub chan struct{} |
|
|
|
|
unsubOnce sync.Once |
|
|
|
|
lastTry mclock.AbsTime |
|
|
|
|
lastSubErr error |
|
|
|
|
waitTime, backoffMax time.Duration |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -149,7 +172,7 @@ func (s *resubscribeSub) subscribe() Subscription { |
|
|
|
|
s.lastTry = mclock.Now() |
|
|
|
|
ctx, cancel := context.WithCancel(context.Background()) |
|
|
|
|
go func() { |
|
|
|
|
rsub, err := s.fn(ctx) |
|
|
|
|
rsub, err := s.fn(ctx, s.lastSubErr) |
|
|
|
|
sub = rsub |
|
|
|
|
subscribed <- err |
|
|
|
|
}() |
|
|
|
@ -178,6 +201,7 @@ func (s *resubscribeSub) waitForError(sub Subscription) bool { |
|
|
|
|
defer sub.Unsubscribe() |
|
|
|
|
select { |
|
|
|
|
case err := <-sub.Err(): |
|
|
|
|
s.lastSubErr = err |
|
|
|
|
return err == nil |
|
|
|
|
case <-s.unsub: |
|
|
|
|
return true |
|
|
|
|