FIRInstallationsBackoffController.m 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * Copyright 2020 Google LLC
  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. #import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsBackoffController.h"
  17. static const NSTimeInterval k24Hours = 24 * 60 * 60;
  18. static const NSTimeInterval k30Minutes = 30 * 60;
  19. /** The class represents `FIRInstallationsBackoffController` state required to calculate next
  20. allowed request time. The properties of the class are intentionally immutable because changing them
  21. separately leads to an inconsistent state. */
  22. @interface FIRInstallationsBackoffEventData : NSObject
  23. @property(nonatomic, readonly) FIRInstallationsBackoffEvent eventType;
  24. @property(nonatomic, readonly) NSDate *lastEventDate;
  25. @property(nonatomic, readonly) NSInteger eventCount;
  26. @property(nonatomic, readonly) NSTimeInterval backoffTimeInterval;
  27. @end
  28. @implementation FIRInstallationsBackoffEventData
  29. - (instancetype)initWithEvent:(FIRInstallationsBackoffEvent)eventType
  30. lastEventDate:(NSDate *)lastEventDate
  31. eventCount:(NSInteger)eventCount {
  32. self = [super init];
  33. if (self) {
  34. _eventType = eventType;
  35. _lastEventDate = lastEventDate;
  36. _eventCount = eventCount;
  37. _backoffTimeInterval = [[self class] backoffTimeIntervalWithEvent:eventType
  38. eventCount:eventCount];
  39. }
  40. return self;
  41. }
  42. + (NSTimeInterval)backoffTimeIntervalWithEvent:(FIRInstallationsBackoffEvent)eventType
  43. eventCount:(NSInteger)eventCount {
  44. switch (eventType) {
  45. case FIRInstallationsBackoffEventSuccess:
  46. return 0;
  47. break;
  48. case FIRInstallationsBackoffEventRecoverableFailure:
  49. return [self recoverableErrorBackoffTimeForAttemptNumber:eventCount];
  50. break;
  51. case FIRInstallationsBackoffEventUnrecoverableFailure:
  52. return k24Hours;
  53. break;
  54. }
  55. }
  56. + (NSTimeInterval)recoverableErrorBackoffTimeForAttemptNumber:(NSInteger)attemptNumber {
  57. NSTimeInterval exponentialInterval = pow(2, attemptNumber) + [self randomMilliseconds];
  58. return MIN(exponentialInterval, k30Minutes);
  59. }
  60. + (NSTimeInterval)randomMilliseconds {
  61. int32_t random_millis = ABS(arc4random() % 1000);
  62. return (double)random_millis * 0.001;
  63. }
  64. @end
  65. @interface FIRInstallationsBackoffController ()
  66. @property(nonatomic, readonly) FIRCurrentDateProvider currentDateProvider;
  67. @property(nonatomic, nullable) FIRInstallationsBackoffEventData *lastEventData;
  68. @end
  69. @implementation FIRInstallationsBackoffController
  70. - (instancetype)init {
  71. return [self initWithCurrentDateProvider:FIRRealCurrentDateProvider()];
  72. }
  73. - (instancetype)initWithCurrentDateProvider:(FIRCurrentDateProvider)currentDateProvider {
  74. self = [super init];
  75. if (self) {
  76. _currentDateProvider = [currentDateProvider copy];
  77. }
  78. return self;
  79. }
  80. - (BOOL)isNextRequestAllowed {
  81. @synchronized(self) {
  82. if (self.lastEventData == nil) {
  83. return YES;
  84. }
  85. NSTimeInterval timeSinceLastEvent =
  86. [self.currentDateProvider() timeIntervalSinceDate:self.lastEventData.lastEventDate];
  87. return timeSinceLastEvent >= self.lastEventData.backoffTimeInterval;
  88. }
  89. }
  90. - (void)registerEvent:(FIRInstallationsBackoffEvent)event {
  91. @synchronized(self) {
  92. // Event of the same type as was registered before.
  93. if (self.lastEventData && self.lastEventData.eventType == event) {
  94. self.lastEventData = [[FIRInstallationsBackoffEventData alloc]
  95. initWithEvent:event
  96. lastEventDate:self.currentDateProvider()
  97. eventCount:self.lastEventData.eventCount + 1];
  98. } else { // A different event.
  99. self.lastEventData =
  100. [[FIRInstallationsBackoffEventData alloc] initWithEvent:event
  101. lastEventDate:self.currentDateProvider()
  102. eventCount:1];
  103. }
  104. }
  105. }
  106. @end