FPRGDTRateLimiter.m 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FirebasePerformance/Sources/Loggers/FPRGDTRateLimiter.h"
  15. #import "FirebasePerformance/Sources/Loggers/FPRGDTRateLimiter+Private.h"
  16. #import <Foundation/Foundation.h>
  17. #import <UIKit/UIKit.h>
  18. #import "FirebasePerformance/Sources/AppActivity/FPRAppActivityTracker.h"
  19. #import "FirebasePerformance/Sources/Common/FPRPerfDate.h"
  20. #import "FirebasePerformance/Sources/Loggers/FPRGDTEvent.h"
  21. #import <GoogleDataTransport/GoogleDataTransport.h>
  22. #import "FirebasePerformance/ProtoSupport/PerfMetric.pbobjc.h"
  23. @interface FPRGDTRateLimiter ()
  24. /**
  25. * Internal date object for setting the time of transformers, which will be used for setting the
  26. * time for trace events and network events.
  27. */
  28. @property(nonatomic) id<FPRDate> date;
  29. @end
  30. @implementation FPRGDTRateLimiter
  31. - (instancetype)initWithDate:(id<FPRDate>)date {
  32. FPRGDTRateLimiter *transformer = [[self.class alloc] init];
  33. transformer.date = date;
  34. transformer.lastTraceEventTime = [date now];
  35. transformer.lastNetworkEventTime = [date now];
  36. return transformer;
  37. }
  38. - (instancetype)init {
  39. self = [super init];
  40. if (self) {
  41. _date = [[FPRPerfDate alloc] init];
  42. // Set lastTraceEventTime to default as this would get reset once we receive the first event.
  43. _lastTraceEventTime = [_date now];
  44. _lastNetworkEventTime = [_date now];
  45. _configurations = [FPRConfigurations sharedInstance];
  46. _allowedTraceEventsCount = [_configurations foregroundEventCount];
  47. _allowedNetworkEventsCount = [_configurations foregroundNetworkEventCount];
  48. if ([FPRAppActivityTracker sharedInstance].applicationState == FPRApplicationStateBackground) {
  49. _allowedTraceEventsCount = [_configurations backgroundEventCount];
  50. _allowedNetworkEventsCount = [_configurations backgroundNetworkEventCount];
  51. }
  52. }
  53. return self;
  54. }
  55. #pragma mark - Transformer methods
  56. /**
  57. * Rate limit PerfMetric Events based on rate limiting logic, event that should be
  58. * dropped will return nil in this transformer.
  59. *
  60. * @param logEvent The event to be evaluated by rate limiting logic.
  61. * @return A transformed event, or nil if the transformation dropped the event.
  62. */
  63. - (GDTCOREvent *)transform:(nonnull GDTCOREvent *)logEvent {
  64. if ([logEvent.dataObject isKindOfClass:[FPRGDTEvent class]]) {
  65. FPRGDTEvent *gdtEvent = (FPRGDTEvent *)logEvent.dataObject;
  66. FPRMSGPerfMetric *perfMetric = gdtEvent.metric;
  67. if (perfMetric.hasTraceMetric) {
  68. FPRMSGTraceMetric *traceMetric = perfMetric.traceMetric;
  69. // If it is an internal trace event, skip rate limiting.
  70. if (traceMetric.isAuto) {
  71. return logEvent;
  72. }
  73. }
  74. }
  75. CGFloat rate = [self resolvedTraceRate];
  76. NSInteger eventCount = self.allowedTraceEventsCount;
  77. NSInteger eventBurstSize = self.traceEventBurstSize;
  78. NSDate *currentTime = [self.date now];
  79. NSTimeInterval interval = [currentTime timeIntervalSinceDate:self.lastTraceEventTime];
  80. if ([self isNetworkEvent:logEvent]) {
  81. rate = [self resolvedNetworkRate];
  82. interval = [currentTime timeIntervalSinceDate:self.lastNetworkEventTime];
  83. eventCount = self.allowedNetworkEventsCount;
  84. eventBurstSize = self.networkEventburstSize;
  85. }
  86. eventCount = [self numberOfAllowedEvents:eventCount
  87. timeInterval:interval
  88. burstSize:eventBurstSize
  89. eventRate:rate];
  90. // Dispatch events only if the allowedEventCount is greater than zero, else drop the event.
  91. if (eventCount > 0) {
  92. if ([self isNetworkEvent:logEvent]) {
  93. self.allowedNetworkEventsCount = --eventCount;
  94. self.lastNetworkEventTime = currentTime;
  95. } else {
  96. self.allowedTraceEventsCount = --eventCount;
  97. self.lastTraceEventTime = currentTime;
  98. }
  99. return logEvent;
  100. }
  101. // Find the type of the log event.
  102. FPRAppActivityTracker *appActivityTracker = [FPRAppActivityTracker sharedInstance];
  103. NSString *counterName = kFPRAppCounterNameTraceEventsRateLimited;
  104. if ([self isNetworkEvent:logEvent]) {
  105. counterName = kFPRAppCounterNameNetworkTraceEventsRateLimited;
  106. }
  107. [appActivityTracker.activeTrace incrementMetric:counterName byInt:1];
  108. return nil;
  109. }
  110. /**
  111. * Calculates the number of allowed events given the time interval, rate and burst size. Token rate
  112. * limiting algorithm implementation.
  113. *
  114. * @param allowedEventsCount Allowed events count on top of which new event count will be added.
  115. * @param timeInterval Time interval for which event count needs to be calculated.
  116. * @param burstSize Maximum number of events that can be allowed at any moment in time.
  117. * @param rate Rate at which events should be added.
  118. * @return Number of allowed events calculated.
  119. */
  120. - (NSInteger)numberOfAllowedEvents:(NSInteger)allowedEventsCount
  121. timeInterval:(NSTimeInterval)timeInterval
  122. burstSize:(NSInteger)burstSize
  123. eventRate:(CGFloat)rate {
  124. NSTimeInterval minutesPassed = timeInterval / 60;
  125. NSInteger newTokens = MAX(0, round(minutesPassed * rate));
  126. NSInteger calculatedAllowedEventsCount = MIN(allowedEventsCount + newTokens, burstSize);
  127. return calculatedAllowedEventsCount;
  128. }
  129. #pragma mark - Trace event rate related methods
  130. /**
  131. * Rate at which the trace events can be accepted for a given log source.
  132. *
  133. * @return Event rate for the log source. This is based on the application's background or
  134. * foreground state.
  135. */
  136. - (CGFloat)resolvedTraceRate {
  137. if (self.overrideRate > 0) {
  138. return self.overrideRate;
  139. }
  140. NSInteger eventCount = [self.configurations foregroundEventCount];
  141. NSInteger timeLimitInMinutes = [self.configurations foregroundEventTimeLimit];
  142. if ([FPRAppActivityTracker sharedInstance].applicationState == FPRApplicationStateBackground) {
  143. eventCount = [self.configurations backgroundEventCount];
  144. timeLimitInMinutes = [self.configurations backgroundEventTimeLimit];
  145. }
  146. CGFloat resolvedRate = eventCount / timeLimitInMinutes;
  147. self.traceEventBurstSize = eventCount;
  148. return resolvedRate;
  149. }
  150. /**
  151. * Rate at which the network events can be accepted for a given log source.
  152. *
  153. * @return Network event rate for the log source. This is based on the application's background or
  154. * foreground state.
  155. */
  156. - (CGFloat)resolvedNetworkRate {
  157. if (self.overrideNetworkRate > 0) {
  158. return self.overrideNetworkRate;
  159. }
  160. NSInteger eventCount = [self.configurations foregroundNetworkEventCount];
  161. NSInteger timeLimitInMinutes = [self.configurations foregroundNetworkEventTimeLimit];
  162. if ([FPRAppActivityTracker sharedInstance].applicationState == FPRApplicationStateBackground) {
  163. eventCount = [self.configurations backgroundNetworkEventCount];
  164. timeLimitInMinutes = [self.configurations backgroundNetworkEventTimeLimit];
  165. }
  166. CGFloat resolvedRate = eventCount / timeLimitInMinutes;
  167. self.networkEventburstSize = eventCount;
  168. return resolvedRate;
  169. }
  170. #pragma mark - Util methods
  171. /**
  172. * Given an event, returns if it is a network event. No, otherwise.
  173. *
  174. * @param logEvent The event to transform.
  175. * @return Yes if the event is a network event. Otherwise, No.
  176. */
  177. - (BOOL)isNetworkEvent:(GDTCOREvent *)logEvent {
  178. if ([logEvent.dataObject isKindOfClass:[FPRGDTEvent class]]) {
  179. FPRGDTEvent *gdtEvent = (FPRGDTEvent *)logEvent.dataObject;
  180. FPRMSGPerfMetric *perfMetric = gdtEvent.metric;
  181. if (perfMetric.hasNetworkRequestMetric) {
  182. return YES;
  183. }
  184. }
  185. return NO;
  186. }
  187. @end