FPRGDTRateLimiter.m 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. @interface FPRGDTRateLimiter ()
  23. /**
  24. * Internal date object for setting the time of transformers, which will be used for setting the
  25. * time for trace events and network events.
  26. */
  27. @property(nonatomic) id<FPRDate> date;
  28. @end
  29. @implementation FPRGDTRateLimiter
  30. - (instancetype)initWithDate:(id<FPRDate>)date {
  31. FPRGDTRateLimiter *transformer = [[self.class alloc] init];
  32. transformer.date = date;
  33. transformer.lastTraceEventTime = [date now];
  34. transformer.lastNetworkEventTime = [date now];
  35. return transformer;
  36. }
  37. - (instancetype)init {
  38. self = [super init];
  39. if (self) {
  40. _date = [[FPRPerfDate alloc] init];
  41. // Set lastTraceEventTime to default as this would get reset once we receive the first event.
  42. _lastTraceEventTime = [_date now];
  43. _lastNetworkEventTime = [_date now];
  44. _configurations = [FPRConfigurations sharedInstance];
  45. _allowedTraceEventsCount = [_configurations foregroundEventCount];
  46. _allowedNetworkEventsCount = [_configurations foregroundNetworkEventCount];
  47. if ([FPRAppActivityTracker sharedInstance].applicationState == FPRApplicationStateBackground) {
  48. _allowedTraceEventsCount = [_configurations backgroundEventCount];
  49. _allowedNetworkEventsCount = [_configurations backgroundNetworkEventCount];
  50. }
  51. }
  52. return self;
  53. }
  54. #pragma mark - Transformer methods
  55. /**
  56. * Rate limit PerfMetric Events based on rate limiting logic, event that should be
  57. * dropped will return nil in this transformer.
  58. *
  59. * @param logEvent The event to be evaluated by rate limiting logic.
  60. * @return A transformed event, or nil if the transformation dropped the event.
  61. */
  62. - (GDTCOREvent *)transformGDTEvent:(nonnull GDTCOREvent *)logEvent {
  63. if ([logEvent.dataObject isKindOfClass:[FPRGDTEvent class]]) {
  64. FPRGDTEvent *gdtEvent = (FPRGDTEvent *)logEvent.dataObject;
  65. firebase_perf_v1_PerfMetric perfMetric = gdtEvent.metric;
  66. if (perfMetric.has_trace_metric) {
  67. firebase_perf_v1_TraceMetric traceMetric = perfMetric.trace_metric;
  68. // If it is an internal trace event, skip rate limiting.
  69. if (traceMetric.is_auto) {
  70. return logEvent;
  71. }
  72. }
  73. }
  74. CGFloat rate = [self resolvedTraceRate];
  75. NSInteger eventCount = self.allowedTraceEventsCount;
  76. NSInteger eventBurstSize = self.traceEventBurstSize;
  77. NSDate *currentTime = [self.date now];
  78. NSTimeInterval interval = [currentTime timeIntervalSinceDate:self.lastTraceEventTime];
  79. if ([self isNetworkEvent:logEvent]) {
  80. rate = [self resolvedNetworkRate];
  81. interval = [currentTime timeIntervalSinceDate:self.lastNetworkEventTime];
  82. eventCount = self.allowedNetworkEventsCount;
  83. eventBurstSize = self.networkEventburstSize;
  84. }
  85. eventCount = [self numberOfAllowedEvents:eventCount
  86. timeInterval:interval
  87. burstSize:eventBurstSize
  88. eventRate:rate];
  89. // Dispatch events only if the allowedEventCount is greater than zero, else drop the event.
  90. if (eventCount > 0) {
  91. if ([self isNetworkEvent:logEvent]) {
  92. self.allowedNetworkEventsCount = --eventCount;
  93. self.lastNetworkEventTime = currentTime;
  94. } else {
  95. self.allowedTraceEventsCount = --eventCount;
  96. self.lastTraceEventTime = currentTime;
  97. }
  98. return logEvent;
  99. }
  100. // Find the type of the log event.
  101. FPRAppActivityTracker *appActivityTracker = [FPRAppActivityTracker sharedInstance];
  102. NSString *counterName = kFPRAppCounterNameTraceEventsRateLimited;
  103. if ([self isNetworkEvent:logEvent]) {
  104. counterName = kFPRAppCounterNameNetworkTraceEventsRateLimited;
  105. }
  106. [appActivityTracker.activeTrace incrementMetric:counterName byInt:1];
  107. return nil;
  108. }
  109. /**
  110. * Calculates the number of allowed events given the time interval, rate and burst size. Token rate
  111. * limiting algorithm implementation.
  112. *
  113. * @param allowedEventsCount Allowed events count on top of which new event count will be added.
  114. * @param timeInterval Time interval for which event count needs to be calculated.
  115. * @param burstSize Maximum number of events that can be allowed at any moment in time.
  116. * @param rate Rate at which events should be added.
  117. * @return Number of allowed events calculated.
  118. */
  119. - (NSInteger)numberOfAllowedEvents:(NSInteger)allowedEventsCount
  120. timeInterval:(NSTimeInterval)timeInterval
  121. burstSize:(NSInteger)burstSize
  122. eventRate:(CGFloat)rate {
  123. NSTimeInterval minutesPassed = timeInterval / 60;
  124. NSInteger newTokens = MAX(0, round(minutesPassed * rate));
  125. NSInteger calculatedAllowedEventsCount = MIN(allowedEventsCount + newTokens, burstSize);
  126. return calculatedAllowedEventsCount;
  127. }
  128. #pragma mark - Trace event rate related methods
  129. /**
  130. * Rate at which the trace events can be accepted for a given log source.
  131. *
  132. * @return Event rate for the log source. This is based on the application's background or
  133. * foreground state.
  134. */
  135. - (CGFloat)resolvedTraceRate {
  136. if (self.overrideRate > 0) {
  137. return self.overrideRate;
  138. }
  139. NSInteger eventCount = [self.configurations foregroundEventCount];
  140. NSInteger timeLimitInMinutes = [self.configurations foregroundEventTimeLimit];
  141. if ([FPRAppActivityTracker sharedInstance].applicationState == FPRApplicationStateBackground) {
  142. eventCount = [self.configurations backgroundEventCount];
  143. timeLimitInMinutes = [self.configurations backgroundEventTimeLimit];
  144. }
  145. CGFloat resolvedRate = eventCount / timeLimitInMinutes;
  146. self.traceEventBurstSize = eventCount;
  147. return resolvedRate;
  148. }
  149. /**
  150. * Rate at which the network events can be accepted for a given log source.
  151. *
  152. * @return Network event rate for the log source. This is based on the application's background or
  153. * foreground state.
  154. */
  155. - (CGFloat)resolvedNetworkRate {
  156. if (self.overrideNetworkRate > 0) {
  157. return self.overrideNetworkRate;
  158. }
  159. NSInteger eventCount = [self.configurations foregroundNetworkEventCount];
  160. NSInteger timeLimitInMinutes = [self.configurations foregroundNetworkEventTimeLimit];
  161. if ([FPRAppActivityTracker sharedInstance].applicationState == FPRApplicationStateBackground) {
  162. eventCount = [self.configurations backgroundNetworkEventCount];
  163. timeLimitInMinutes = [self.configurations backgroundNetworkEventTimeLimit];
  164. }
  165. CGFloat resolvedRate = eventCount / timeLimitInMinutes;
  166. self.networkEventburstSize = eventCount;
  167. return resolvedRate;
  168. }
  169. #pragma mark - Util methods
  170. /**
  171. * Given an event, returns if it is a network event. No, otherwise.
  172. *
  173. * @param logEvent The event to transform.
  174. * @return Yes if the event is a network event. Otherwise, No.
  175. */
  176. - (BOOL)isNetworkEvent:(GDTCOREvent *)logEvent {
  177. if ([logEvent.dataObject isKindOfClass:[FPRGDTEvent class]]) {
  178. FPRGDTEvent *gdtEvent = (FPRGDTEvent *)logEvent.dataObject;
  179. firebase_perf_v1_PerfMetric perfMetric = gdtEvent.metric;
  180. if (perfMetric.has_network_request_metric) {
  181. return YES;
  182. }
  183. }
  184. return NO;
  185. }
  186. @end