Skip to content

Commit

Permalink
Merge pull request etcd-io#17641 from sheyt0/v3.5.13
Browse files Browse the repository at this point in the history
[3.5] backport for fix retry requests when receiving ErrGPRCNotSupportedForLearner
  • Loading branch information
ahrtr committed Apr 4, 2024
2 parents 130cfe9 + 0c2f208 commit f5092bc
Showing 1 changed file with 16 additions and 4 deletions.
20 changes: 16 additions & 4 deletions client/v3/retry_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package clientv3

import (
"context"
"errors"
"io"
"sync"
"time"
Expand Down Expand Up @@ -85,7 +86,7 @@ func (c *Client) unaryClientInterceptor(optFuncs ...retryOption) grpc.UnaryClien
}
continue
}
if !isSafeRetry(c.lg, lastErr, callOpts) {
if !isSafeRetry(c, lastErr, callOpts) {
return lastErr
}
}
Expand Down Expand Up @@ -279,7 +280,7 @@ func (s *serverStreamingRetryingStream) receiveMsgAndIndicateRetry(m interface{}
return true, err

}
return isSafeRetry(s.client.lg, err, s.callOpts), err
return isSafeRetry(s.client, err, s.callOpts), err
}

func (s *serverStreamingRetryingStream) reestablishStreamAndResendBuffer(callCtx context.Context) (grpc.ClientStream, error) {
Expand Down Expand Up @@ -319,17 +320,28 @@ func waitRetryBackoff(ctx context.Context, attempt uint, callOpts *options) erro
}

// isSafeRetry returns "true", if request is safe for retry with the given error.
func isSafeRetry(lg *zap.Logger, err error, callOpts *options) bool {
func isSafeRetry(c *Client, err error, callOpts *options) bool {
if isContextError(err) {
return false
}

// Situation when learner refuses RPC it is supposed to not serve is from the server
// perspective not retryable.
// But for backward-compatibility reasons we need to support situation that
// customer provides mix of learners (not yet voters) and voters with an
// expectation to pick voter in the next attempt.
// TODO: Ideally client should be 'aware' which endpoint represents: leader/voter/learner with high probability.
if errors.Is(err, rpctypes.ErrGPRCNotSupportedForLearner) && len(c.Endpoints()) > 1 {
return true
}

switch callOpts.retryPolicy {
case repeatable:
return isSafeRetryImmutableRPC(err)
case nonRepeatable:
return isSafeRetryMutableRPC(err)
default:
lg.Warn("unrecognized retry policy", zap.String("retryPolicy", callOpts.retryPolicy.String()))
c.lg.Warn("unrecognized retry policy", zap.String("retryPolicy", callOpts.retryPolicy.String()))
return false
}
}
Expand Down

0 comments on commit f5092bc

Please sign in to comment.