backoff.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /*
  2. *
  3. * Copyright 2017 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. // Package backoff implement the backoff strategy for gRPC.
  19. //
  20. // This is kept in internal until the gRPC project decides whether or not to
  21. // allow alternative backoff strategies.
  22. package backoff
  23. import (
  24. "context"
  25. "errors"
  26. "time"
  27. grpcbackoff "google.golang.org/grpc/backoff"
  28. "google.golang.org/grpc/internal/grpcrand"
  29. )
  30. // Strategy defines the methodology for backing off after a grpc connection
  31. // failure.
  32. type Strategy interface {
  33. // Backoff returns the amount of time to wait before the next retry given
  34. // the number of consecutive failures.
  35. Backoff(retries int) time.Duration
  36. }
  37. // DefaultExponential is an exponential backoff implementation using the
  38. // default values for all the configurable knobs defined in
  39. // https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
  40. var DefaultExponential = Exponential{Config: grpcbackoff.DefaultConfig}
  41. // Exponential implements exponential backoff algorithm as defined in
  42. // https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
  43. type Exponential struct {
  44. // Config contains all options to configure the backoff algorithm.
  45. Config grpcbackoff.Config
  46. }
  47. // Backoff returns the amount of time to wait before the next retry given the
  48. // number of retries.
  49. func (bc Exponential) Backoff(retries int) time.Duration {
  50. if retries == 0 {
  51. return bc.Config.BaseDelay
  52. }
  53. backoff, max := float64(bc.Config.BaseDelay), float64(bc.Config.MaxDelay)
  54. for backoff < max && retries > 0 {
  55. backoff *= bc.Config.Multiplier
  56. retries--
  57. }
  58. if backoff > max {
  59. backoff = max
  60. }
  61. // Randomize backoff delays so that if a cluster of requests start at
  62. // the same time, they won't operate in lockstep.
  63. backoff *= 1 + bc.Config.Jitter*(grpcrand.Float64()*2-1)
  64. if backoff < 0 {
  65. return 0
  66. }
  67. return time.Duration(backoff)
  68. }
  69. // ErrResetBackoff is the error to be returned by the function executed by RunF,
  70. // to instruct the latter to reset its backoff state.
  71. var ErrResetBackoff = errors.New("reset backoff state")
  72. // RunF provides a convenient way to run a function f repeatedly until the
  73. // context expires or f returns a non-nil error that is not ErrResetBackoff.
  74. // When f returns ErrResetBackoff, RunF continues to run f, but resets its
  75. // backoff state before doing so. backoff accepts an integer representing the
  76. // number of retries, and returns the amount of time to backoff.
  77. func RunF(ctx context.Context, f func() error, backoff func(int) time.Duration) {
  78. attempt := 0
  79. timer := time.NewTimer(0)
  80. for ctx.Err() == nil {
  81. select {
  82. case <-timer.C:
  83. case <-ctx.Done():
  84. timer.Stop()
  85. return
  86. }
  87. err := f()
  88. if errors.Is(err, ErrResetBackoff) {
  89. timer.Reset(0)
  90. attempt = 0
  91. continue
  92. }
  93. if err != nil {
  94. return
  95. }
  96. timer.Reset(backoff(attempt))
  97. attempt++
  98. }
  99. }