From 428fb9623a46a7879cf057ccf9fdd083679512dc Mon Sep 17 00:00:00 2001 From: Hisanobu Tomari Date: Sat, 8 Oct 2022 06:57:30 +0900 Subject: [PATCH] client/v3: Add backoff before retry when watch stream returns unavailable The client retries connection without backoff when the server is gone after the watch stream is established. This results in high CPU usage in the client process. This change introduces backoff when the stream is failed and unavailable. Signed-off-by: Hisanobu Tomari --- client/v3/watch.go | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/client/v3/watch.go b/client/v3/watch.go index adf2efbcd..44100d042 100644 --- a/client/v3/watch.go +++ b/client/v3/watch.go @@ -540,6 +540,7 @@ func (w *watchGrpcStream) run() { cancelSet := make(map[int64]struct{}) var cur *pb.WatchResponse + backoff := time.Millisecond for { select { // Watch() requested @@ -676,6 +677,7 @@ func (w *watchGrpcStream) run() { closeErr = err return } + backoff = w.backoffIfUnavailable(backoff, err) if wc, closeErr = w.newWatchClient(); closeErr != nil { return } @@ -995,6 +997,21 @@ func (w *watchGrpcStream) joinSubstreams() { var maxBackoff = 100 * time.Millisecond +func (w *watchGrpcStream) backoffIfUnavailable(backoff time.Duration, err error) time.Duration { + if isUnavailableErr(w.ctx, err) { + // retry, but backoff + if backoff < maxBackoff { + // 25% backoff factor + backoff = backoff + backoff/4 + if backoff > maxBackoff { + backoff = maxBackoff + } + } + time.Sleep(backoff) + } + return backoff +} + // openWatchClient retries opening a watch client until success or halt. // manually retry in case "ws==nil && err==nil" // TODO: remove FailFast=false @@ -1015,17 +1032,7 @@ func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error) if isHaltErr(w.ctx, err) { return nil, v3rpc.Error(err) } - if isUnavailableErr(w.ctx, err) { - // retry, but backoff - if backoff < maxBackoff { - // 25% backoff factor - backoff = backoff + backoff/4 - if backoff > maxBackoff { - backoff = maxBackoff - } - } - time.Sleep(backoff) - } + backoff = w.backoffIfUnavailable(backoff, err) } return ws, nil }