RetryDelaySequence.swift 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /*
  2. * Copyright 2023, 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. #if canImport(Darwin)
  17. public import Darwin // should be @usableFromInline
  18. #elseif canImport(Android)
  19. public import Android // should be @usableFromInline
  20. #elseif canImport(Glibc)
  21. public import Glibc // should be @usableFromInline
  22. #elseif canImport(Musl)
  23. public import Musl // should be @usableFromInline
  24. #else
  25. #error("Unsupported OS")
  26. #endif
  27. @available(gRPCSwift 2.0, *)
  28. @usableFromInline
  29. struct RetryDelaySequence: Sequence {
  30. @usableFromInline
  31. typealias Element = Duration
  32. @usableFromInline
  33. let policy: RetryPolicy
  34. @inlinable
  35. init(policy: RetryPolicy) {
  36. self.policy = policy
  37. }
  38. @inlinable
  39. func makeIterator() -> Iterator {
  40. Iterator(policy: self.policy)
  41. }
  42. @usableFromInline
  43. struct Iterator: IteratorProtocol {
  44. @usableFromInline
  45. let policy: RetryPolicy
  46. @usableFromInline
  47. private(set) var n = 1
  48. @inlinable
  49. init(policy: RetryPolicy) {
  50. self.policy = policy
  51. }
  52. @inlinable
  53. var _initialBackoffSeconds: Double {
  54. Self._durationToTimeInterval(self.policy.initialBackoff)
  55. }
  56. @inlinable
  57. var _maxBackoffSeconds: Double {
  58. Self._durationToTimeInterval(self.policy.maxBackoff)
  59. }
  60. @inlinable
  61. mutating func next() -> Duration? {
  62. defer { self.n += 1 }
  63. /// The nth retry will happen after a randomly chosen delay between zero and
  64. /// `min(initialBackoff * backoffMultiplier^(n-1), maxBackoff)`.
  65. let factor = pow(self.policy.backoffMultiplier, Double(self.n - 1))
  66. let computedBackoff = self._initialBackoffSeconds * factor
  67. let clampedBackoff = Swift.min(computedBackoff, self._maxBackoffSeconds)
  68. let randomisedBackoff = Double.random(in: 0.0 ... clampedBackoff)
  69. return Self._timeIntervalToDuration(randomisedBackoff)
  70. }
  71. @inlinable
  72. static func _timeIntervalToDuration(_ seconds: Double) -> Duration {
  73. let secondsComponent = Int64(seconds)
  74. let attoseconds = (seconds - Double(secondsComponent)) * 1e18
  75. let attosecondsComponent = Int64(attoseconds)
  76. return Duration(
  77. secondsComponent: secondsComponent,
  78. attosecondsComponent: attosecondsComponent
  79. )
  80. }
  81. @inlinable
  82. static func _durationToTimeInterval(_ duration: Duration) -> Double {
  83. var seconds = Double(duration.components.seconds)
  84. seconds += (Double(duration.components.attoseconds) / 1e18)
  85. return seconds
  86. }
  87. }
  88. }