github.com/cloudwego/kitex@v0.9.0/pkg/retry/failure.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package retry
    18  
    19  import (
    20  	"fmt"
    21  	"time"
    22  
    23  	"github.com/bytedance/gopkg/lang/fastrand"
    24  
    25  	"github.com/cloudwego/kitex/pkg/rpcinfo"
    26  )
    27  
    28  const maxFailureRetryTimes = 5
    29  
    30  // NewFailurePolicy init default failure retry policy
    31  func NewFailurePolicy() *FailurePolicy {
    32  	p := &FailurePolicy{
    33  		StopPolicy: StopPolicy{
    34  			MaxRetryTimes:    2,
    35  			DisableChainStop: false,
    36  			CBPolicy: CBPolicy{
    37  				ErrorRate: defaultCBErrRate,
    38  			},
    39  		},
    40  		BackOffPolicy: &BackOffPolicy{BackOffType: NoneBackOffType},
    41  	}
    42  	return p
    43  }
    44  
    45  // NewFailurePolicyWithResultRetry init failure retry policy with ShouldResultRetry
    46  func NewFailurePolicyWithResultRetry(rr *ShouldResultRetry) *FailurePolicy {
    47  	fp := NewFailurePolicy()
    48  	fp.ShouldResultRetry = rr
    49  	return fp
    50  }
    51  
    52  // WithMaxRetryTimes default is 2, not include first call
    53  func (p *FailurePolicy) WithMaxRetryTimes(retryTimes int) {
    54  	if retryTimes < 0 || retryTimes > maxFailureRetryTimes {
    55  		panic(fmt.Errorf("maxFailureRetryTimes is %d", maxFailureRetryTimes))
    56  	}
    57  	p.StopPolicy.MaxRetryTimes = retryTimes
    58  }
    59  
    60  // WithMaxDurationMS include first call and retry call, if the total duration exceed limit then stop retry
    61  func (p *FailurePolicy) WithMaxDurationMS(maxMS uint32) {
    62  	p.StopPolicy.MaxDurationMS = maxMS
    63  }
    64  
    65  // DisableChainRetryStop disables chain stop
    66  func (p *FailurePolicy) DisableChainRetryStop() {
    67  	p.StopPolicy.DisableChainStop = true
    68  }
    69  
    70  // WithDDLStop sets ddl stop to true.
    71  func (p *FailurePolicy) WithDDLStop() {
    72  	p.StopPolicy.DDLStop = true
    73  }
    74  
    75  // WithFixedBackOff set fixed time.
    76  func (p *FailurePolicy) WithFixedBackOff(fixMS int) {
    77  	if err := checkFixedBackOff(fixMS); err != nil {
    78  		panic(err)
    79  	}
    80  	p.BackOffPolicy = &BackOffPolicy{FixedBackOffType, make(map[BackOffCfgKey]float64, 1)}
    81  	p.BackOffPolicy.CfgItems[FixMSBackOffCfgKey] = float64(fixMS)
    82  }
    83  
    84  // WithRandomBackOff sets the random time.
    85  func (p *FailurePolicy) WithRandomBackOff(minMS, maxMS int) {
    86  	if err := checkRandomBackOff(minMS, maxMS); err != nil {
    87  		panic(err)
    88  	}
    89  	p.BackOffPolicy = &BackOffPolicy{RandomBackOffType, make(map[BackOffCfgKey]float64, 2)}
    90  	p.BackOffPolicy.CfgItems[MinMSBackOffCfgKey] = float64(minMS)
    91  	p.BackOffPolicy.CfgItems[MaxMSBackOffCfgKey] = float64(maxMS)
    92  }
    93  
    94  // WithRetryBreaker sets the error rate.
    95  func (p *FailurePolicy) WithRetryBreaker(errRate float64) {
    96  	p.StopPolicy.CBPolicy.ErrorRate = errRate
    97  	if err := checkCBErrorRate(&p.StopPolicy.CBPolicy); err != nil {
    98  		panic(err)
    99  	}
   100  }
   101  
   102  // WithRetrySameNode sets to retry the same node.
   103  func (p *FailurePolicy) WithRetrySameNode() {
   104  	p.RetrySameNode = true
   105  }
   106  
   107  // WithSpecifiedResultRetry sets customized ShouldResultRetry.
   108  func (p *FailurePolicy) WithSpecifiedResultRetry(rr *ShouldResultRetry) {
   109  	if rr != nil {
   110  		p.ShouldResultRetry = rr
   111  	}
   112  }
   113  
   114  // String prints human readable information.
   115  func (p FailurePolicy) String() string {
   116  	return fmt.Sprintf("{StopPolicy:%+v BackOffPolicy:%+v RetrySameNode:%+v "+
   117  		"ShouldResultRetry:{ErrorRetry:%t, RespRetry:%t}}", p.StopPolicy, p.BackOffPolicy, p.RetrySameNode, p.IsErrorRetryNonNil(), p.IsRespRetryNonNil())
   118  }
   119  
   120  // AllErrorRetry is common choice for ShouldResultRetry.
   121  func AllErrorRetry() *ShouldResultRetry {
   122  	return &ShouldResultRetry{ErrorRetry: func(err error, ri rpcinfo.RPCInfo) bool {
   123  		return err != nil
   124  	}}
   125  }
   126  
   127  // BackOff is the interface of back off implements
   128  type BackOff interface {
   129  	Wait(callTimes int)
   130  }
   131  
   132  // NoneBackOff means there's no backoff.
   133  var NoneBackOff = &noneBackOff{}
   134  
   135  func newFixedBackOff(fixMS int) BackOff {
   136  	return &fixedBackOff{
   137  		fixMS: fixMS,
   138  	}
   139  }
   140  
   141  func newRandomBackOff(minMS, maxMS int) BackOff {
   142  	return &randomBackOff{
   143  		minMS: minMS,
   144  		maxMS: maxMS,
   145  	}
   146  }
   147  
   148  type noneBackOff struct{}
   149  
   150  // String prints human readable information.
   151  func (p noneBackOff) String() string {
   152  	return "NoneBackOff"
   153  }
   154  
   155  // Wait implements the BackOff interface.
   156  func (p *noneBackOff) Wait(callTimes int) {
   157  }
   158  
   159  type fixedBackOff struct {
   160  	fixMS int
   161  }
   162  
   163  // Wait implements the BackOff interface.
   164  func (p *fixedBackOff) Wait(callTimes int) {
   165  	time.Sleep(time.Duration(p.fixMS) * time.Millisecond)
   166  }
   167  
   168  // String prints human readable information.
   169  func (p fixedBackOff) String() string {
   170  	return fmt.Sprintf("FixedBackOff(%dms)", p.fixMS)
   171  }
   172  
   173  type randomBackOff struct {
   174  	minMS int
   175  	maxMS int
   176  }
   177  
   178  func (p *randomBackOff) randomDuration() time.Duration {
   179  	return time.Duration(p.minMS+fastrand.Intn(p.maxMS-p.minMS)) * time.Millisecond
   180  }
   181  
   182  // Wait implements the BackOff interface.
   183  func (p *randomBackOff) Wait(callTimes int) {
   184  	time.Sleep(p.randomDuration())
   185  }
   186  
   187  // String prints human readable information.
   188  func (p randomBackOff) String() string {
   189  	return fmt.Sprintf("RandomBackOff(%dms-%dms)", p.minMS, p.maxMS)
   190  }
   191  
   192  func checkFixedBackOff(fixMS int) error {
   193  	if fixMS == 0 {
   194  		return fmt.Errorf("invalid FixedBackOff, fixMS=%d", fixMS)
   195  	}
   196  	return nil
   197  }
   198  
   199  func checkRandomBackOff(minMS, maxMS int) error {
   200  	if maxMS <= minMS {
   201  		return fmt.Errorf("invalid RandomBackOff, minMS=%d, maxMS=%d", minMS, maxMS)
   202  	}
   203  	return nil
   204  }