ConnectionBackoff.swift 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. /*
  2. * Copyright 2024, gRPC Authors All rights reserved.
  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. @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  17. struct ConnectionBackoff {
  18. var initial: Duration
  19. var max: Duration
  20. var multiplier: Double
  21. var jitter: Double
  22. init(initial: Duration, max: Duration, multiplier: Double, jitter: Double) {
  23. self.initial = initial
  24. self.max = max
  25. self.multiplier = multiplier
  26. self.jitter = jitter
  27. }
  28. func makeIterator() -> Iterator {
  29. return Iterator(self)
  30. }
  31. // Deliberately not conforming to `IteratorProtocol` as `next()` never returns `nil` which
  32. // isn't expressible via `IteratorProtocol`.
  33. struct Iterator {
  34. private var isInitial: Bool
  35. private var currentBackoffSeconds: Double
  36. private let jitter: Double
  37. private let multiplier: Double
  38. private let maxBackoffSeconds: Double
  39. init(_ backoff: ConnectionBackoff) {
  40. self.isInitial = true
  41. self.currentBackoffSeconds = Self.seconds(from: backoff.initial)
  42. self.jitter = backoff.jitter
  43. self.multiplier = backoff.multiplier
  44. self.maxBackoffSeconds = Self.seconds(from: backoff.max)
  45. }
  46. private static func seconds(from duration: Duration) -> Double {
  47. var seconds = Double(duration.components.seconds)
  48. seconds += Double(duration.components.attoseconds) / 1e18
  49. return seconds
  50. }
  51. private static func duration(from seconds: Double) -> Duration {
  52. let nanoseconds = seconds * 1e9
  53. let wholeNanos = Int64(nanoseconds)
  54. return .nanoseconds(wholeNanos)
  55. }
  56. mutating func next() -> Duration {
  57. // The initial backoff doesn't get jittered.
  58. if self.isInitial {
  59. self.isInitial = false
  60. return Self.duration(from: self.currentBackoffSeconds)
  61. }
  62. // Scale up the last backoff.
  63. self.currentBackoffSeconds *= self.multiplier
  64. // Limit it to the max backoff.
  65. if self.currentBackoffSeconds > self.maxBackoffSeconds {
  66. self.currentBackoffSeconds = self.maxBackoffSeconds
  67. }
  68. let backoff = self.currentBackoffSeconds
  69. let jitter = Double.random(in: -(self.jitter * backoff) ... self.jitter * backoff)
  70. let jitteredBackoff = backoff + jitter
  71. return Self.duration(from: jitteredBackoff)
  72. }
  73. }
  74. }