github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/go-grpc-middleware/retry/options.go (about) 1 // Copyright 2016 Michal Witkowski. All Rights Reserved. 2 // See LICENSE for licensing terms. 3 4 package grpc_retry 5 6 import ( 7 "time" 8 9 "github.com/hxx258456/ccgo/grpc" 10 "github.com/hxx258456/ccgo/grpc/codes" 11 "github.com/hxx258456/ccgo/net/context" 12 ) 13 14 var ( 15 // DefaultRetriableCodes is a set of well known types gRPC codes that should be retri-able. 16 // 17 // `ResourceExhausted` means that the user quota, e.g. per-RPC limits, have been reached. 18 // `Unavailable` means that system is currently unavailable and the client should retry again. 19 DefaultRetriableCodes = []codes.Code{codes.ResourceExhausted, codes.Unavailable} 20 21 defaultOptions = &options{ 22 max: 0, // disabed 23 perCallTimeout: 0, // disabled 24 includeHeader: true, 25 codes: DefaultRetriableCodes, 26 backoffFunc: BackoffFuncContext(func(ctx context.Context, attempt uint) time.Duration { 27 return BackoffLinearWithJitter(50*time.Millisecond /*jitter*/, 0.10)(attempt) 28 }), 29 } 30 ) 31 32 // BackoffFunc denotes a family of functions that control the backoff duration between call retries. 33 // 34 // They are called with an identifier of the attempt, and should return a time the system client should 35 // hold off for. If the time returned is longer than the `context.Context.Deadline` of the request 36 // the deadline of the request takes precedence and the wait will be interrupted before proceeding 37 // with the next iteration. 38 type BackoffFunc func(attempt uint) time.Duration 39 40 // BackoffFuncContext denotes a family of functions that control the backoff duration between call retries. 41 // 42 // They are called with an identifier of the attempt, and should return a time the system client should 43 // hold off for. If the time returned is longer than the `context.Context.Deadline` of the request 44 // the deadline of the request takes precedence and the wait will be interrupted before proceeding 45 // with the next iteration. The context can be used to extract request scoped metadata and context values. 46 type BackoffFuncContext func(ctx context.Context, attempt uint) time.Duration 47 48 // Disable disables the retry behaviour on this call, or this interceptor. 49 // 50 // Its semantically the same to `WithMax` 51 func Disable() CallOption { 52 return WithMax(0) 53 } 54 55 // WithMax sets the maximum number of retries on this call, or this interceptor. 56 func WithMax(maxRetries uint) CallOption { 57 return CallOption{applyFunc: func(o *options) { 58 o.max = maxRetries 59 }} 60 } 61 62 // WithBackoff sets the `BackoffFunc` used to control time between retries. 63 func WithBackoff(bf BackoffFunc) CallOption { 64 return CallOption{applyFunc: func(o *options) { 65 o.backoffFunc = BackoffFuncContext(func(ctx context.Context, attempt uint) time.Duration { 66 return bf(attempt) 67 }) 68 }} 69 } 70 71 // WithBackoffContext sets the `BackoffFuncContext` used to control time between retries. 72 func WithBackoffContext(bf BackoffFuncContext) CallOption { 73 return CallOption{applyFunc: func(o *options) { 74 o.backoffFunc = bf 75 }} 76 } 77 78 // WithCodes sets which codes should be retried. 79 // 80 // Please *use with care*, as you may be retrying non-idempotend calls. 81 // 82 // You cannot automatically retry on Cancelled and Deadline, please use `WithPerRetryTimeout` for these. 83 func WithCodes(retryCodes ...codes.Code) CallOption { 84 return CallOption{applyFunc: func(o *options) { 85 o.codes = retryCodes 86 }} 87 } 88 89 // WithPerRetryTimeout sets the RPC timeout per call (including initial call) on this call, or this interceptor. 90 // 91 // The context.Deadline of the call takes precedence and sets the maximum time the whole invocation 92 // will take, but WithPerRetryTimeout can be used to limit the RPC time per each call. 93 // 94 // For example, with context.Deadline = now + 10s, and WithPerRetryTimeout(3 * time.Seconds), each 95 // of the retry calls (including the initial one) will have a deadline of now + 3s. 96 // 97 // A value of 0 disables the timeout overrides completely and returns to each retry call using the 98 // parent `context.Deadline`. 99 func WithPerRetryTimeout(timeout time.Duration) CallOption { 100 return CallOption{applyFunc: func(o *options) { 101 o.perCallTimeout = timeout 102 }} 103 } 104 105 type options struct { 106 max uint 107 perCallTimeout time.Duration 108 includeHeader bool 109 codes []codes.Code 110 backoffFunc BackoffFuncContext 111 } 112 113 // CallOption is a grpc.CallOption that is local to grpc_retry. 114 type CallOption struct { 115 grpc.EmptyCallOption // make sure we implement private after() and before() fields so we don't panic. 116 applyFunc func(opt *options) 117 } 118 119 func reuseOrNewWithCallOptions(opt *options, callOptions []CallOption) *options { 120 if len(callOptions) == 0 { 121 return opt 122 } 123 optCopy := &options{} 124 *optCopy = *opt 125 for _, f := range callOptions { 126 f.applyFunc(optCopy) 127 } 128 return optCopy 129 } 130 131 func filterCallOptions(callOptions []grpc.CallOption) (grpcOptions []grpc.CallOption, retryOptions []CallOption) { 132 for _, opt := range callOptions { 133 if co, ok := opt.(CallOption); ok { 134 retryOptions = append(retryOptions, co) 135 } else { 136 grpcOptions = append(grpcOptions, opt) 137 } 138 } 139 return grpcOptions, retryOptions 140 }