Skip to content

Commit 95830b2

Browse files
authored
Create a random delay before initiating a remote config fetch. (#8593)
1 parent 4cf4e7c commit 95830b2

File tree

8 files changed

+100
-15
lines changed

8 files changed

+100
-15
lines changed

FirebasePerformance/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Pending
2+
* Create a random number of delay for remote config fetch during app starts.
3+
14
# Version 8.6.1
25
* Fix the case where the event were dropped for missing a critical field in the event.
36

FirebasePerformance/Sources/Configurations/FPRRemoteConfigFlags+Private.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ static NSString *const kFPRConfigPrefix = @"com.fireperf";
2424
static NSInteger const kFPRConfigFetchIntervalInSeconds = 12 * 60 * 60;
2525

2626
/** Interval after which the configurations can be fetched. Specified in seconds. */
27-
static NSInteger const kFPRConfigAppStartDelayInSeconds = 1 * 30;
27+
static NSInteger const kFPRMinAppStartConfigFetchDelayInSeconds = 5;
2828

2929
/** This extension should only be used for testing. */
3030
@interface FPRRemoteConfigFlags ()
@@ -33,11 +33,20 @@ static NSInteger const kFPRConfigAppStartDelayInSeconds = 1 * 30;
3333
@property(nonatomic) FIRRemoteConfig *fprRemoteConfig;
3434

3535
/** @brief Last activated time of the configurations. */
36-
@property(atomic) NSDate *lastFetchedTime;
36+
@property(atomic, nullable) NSDate *lastFetchedTime;
3737

3838
/** @brief User defaults used for caching. */
3939
@property(nonatomic) NSUserDefaults *userDefaults;
4040

41+
/** @brief Last activated time of the configurations. */
42+
@property(nonatomic) NSDate *applicationStartTime;
43+
44+
/** @brief Number of seconds delayed until the first config is made during app start. */
45+
@property(nonatomic) NSTimeInterval appStartConfigFetchDelayInSeconds;
46+
47+
/** @brief Status of the last remote config fetch. */
48+
@property(nonatomic) FIRRemoteConfigFetchStatus lastFetchStatus;
49+
4150
/**
4251
* Creates an instance of FPRRemoteConfigFlags.
4352
*

FirebasePerformance/Sources/Configurations/FPRRemoteConfigFlags.m

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#define ONE_DAY_SECONDS 24 * 60 * 60
2525

26+
static NSDate *FPRAppStartTime = nil;
27+
2628
typedef NS_ENUM(NSInteger, FPRConfigValueType) {
2729
// Config value type String.
2830
FPRConfigValueTypeString,
@@ -45,13 +47,14 @@ @interface FPRRemoteConfigFlags ()
4547
/** @brief Last time the configs were cached. */
4648
@property(nonatomic) NSDate *lastCachedTime;
4749

48-
/** @brief Status of the last remote config fetch. */
49-
@property(nonatomic) FIRRemoteConfigFetchStatus lastFetchStatus;
50-
5150
@end
5251

5352
@implementation FPRRemoteConfigFlags
5453

54+
+ (void)load {
55+
FPRAppStartTime = [NSDate date];
56+
}
57+
5558
+ (nullable instancetype)sharedInstance {
5659
static FPRRemoteConfigFlags *instance = nil;
5760
static dispatch_once_t onceToken;
@@ -70,6 +73,11 @@ - (instancetype)initWithRemoteConfig:(FIRRemoteConfig *)config {
7073
_userDefaults = [FPRConfigurations sharedInstance].userDefaults;
7174
self.fetchInProgress = NO;
7275

76+
// Set the overall delay to 5+random(25) making the config fetch delay at a max of 30 seconds
77+
self.applicationStartTime = FPRAppStartTime;
78+
self.appStartConfigFetchDelayInSeconds =
79+
kFPRMinAppStartConfigFetchDelayInSeconds + arc4random_uniform(25);
80+
7381
NSMutableDictionary<NSString *, NSNumber *> *keysToCache =
7482
[[NSMutableDictionary<NSString *, NSNumber *> alloc] init];
7583
[keysToCache setObject:@(FPRConfigValueTypeInteger) forKey:@"fpr_log_source"];
@@ -110,8 +118,10 @@ - (void)update {
110118

111119
NSTimeInterval timeIntervalSinceLastFetch =
112120
[self.fprRemoteConfig.lastFetchTime timeIntervalSinceNow];
113-
if (!self.fprRemoteConfig.lastFetchTime ||
114-
ABS(timeIntervalSinceLastFetch) > kFPRConfigFetchIntervalInSeconds) {
121+
NSTimeInterval timeSinceAppStart = [self.applicationStartTime timeIntervalSinceNow];
122+
if ((ABS(timeSinceAppStart) > self.appStartConfigFetchDelayInSeconds) &&
123+
(!self.fprRemoteConfig.lastFetchTime ||
124+
ABS(timeIntervalSinceLastFetch) > kFPRConfigFetchIntervalInSeconds)) {
115125
self.fetchInProgress = YES;
116126
[self.fprRemoteConfig
117127
fetchAndActivateWithCompletionHandler:^(FIRRemoteConfigFetchAndActivateStatus status,

FirebasePerformance/Tests/Unit/Configurations/FPRFakeRemoteConfig.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ - (void)fetchAndActivateWithCompletionHandler:
3232
(FIRRemoteConfigFetchAndActivateCompletion)completionHandler {
3333
if (self.fetchStatus != FIRRemoteConfigFetchAndActivateStatusError) {
3434
self.lastFetchTime = [NSDate date];
35+
self.lastFetchStatus = FIRRemoteConfigFetchStatusSuccess;
3536
}
3637
completionHandler(self.fetchStatus, nil);
3738
}

FirebasePerformance/Tests/Unit/Configurations/FPRRemoteConfigFlagsTest.m

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ - (void)testCacheResetAfterEverySuccessfulFetch {
6464
// Trigger the RC config fetch
6565
remoteConfig.fetchStatus = FIRRemoteConfigFetchStatusSuccess;
6666
remoteConfig.lastFetchTime = nil;
67+
configFlags.appStartConfigFetchDelayInSeconds = 0.0;
6768
[configFlags update];
6869

6970
// Verify the expected remote config values
@@ -109,10 +110,63 @@ - (void)testConfigUpdate {
109110
FPRRemoteConfigFlags *configFlags =
110111
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
111112
remoteConfig.fetchStatus = FIRRemoteConfigFetchStatusSuccess;
113+
configFlags.appStartConfigFetchDelayInSeconds = 0.0;
112114
[configFlags update];
113115
XCTAssertNotNil(configFlags.lastFetchedTime);
114116
}
115117

118+
/** Validate the configuration update does not happen during app start. */
119+
- (void)testConfigFetchDoesNotHappenDuringAppStart {
120+
FPRFakeRemoteConfig *remoteConfig = [[FPRFakeRemoteConfig alloc] init];
121+
remoteConfig.lastFetchTime = nil;
122+
123+
NSTimeInterval appStartConfigFetchDelay = 5.0;
124+
FPRRemoteConfigFlags *configFlags =
125+
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
126+
remoteConfig.lastFetchTime = nil;
127+
configFlags.lastFetchedTime = nil;
128+
configFlags.applicationStartTime = [NSDate date];
129+
configFlags.appStartConfigFetchDelayInSeconds = appStartConfigFetchDelay;
130+
configFlags.lastFetchStatus = FIRRemoteConfigFetchStatusNoFetchYet;
131+
132+
XCTestExpectation *expectation =
133+
[self expectationWithDescription:@"Dummy expectation to wait for the fetch delay."];
134+
dispatch_after(
135+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)((appStartConfigFetchDelay - 2) * NSEC_PER_SEC)),
136+
dispatch_get_main_queue(), ^{
137+
[configFlags update];
138+
[expectation fulfill];
139+
XCTAssertTrue(configFlags.lastFetchStatus == FIRRemoteConfigFetchStatusNoFetchYet);
140+
});
141+
[self waitForExpectationsWithTimeout:(appStartConfigFetchDelay) handler:nil];
142+
}
143+
144+
/** Validate the configuration update happens after a delay during app start. */
145+
- (void)testConfigFetchAfterDelayDuringAppStart {
146+
FPRFakeRemoteConfig *remoteConfig = [[FPRFakeRemoteConfig alloc] init];
147+
remoteConfig.lastFetchTime = nil;
148+
149+
NSTimeInterval appStartConfigFetchDelay = 3.0;
150+
FPRRemoteConfigFlags *configFlags =
151+
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
152+
remoteConfig.lastFetchTime = nil;
153+
configFlags.lastFetchedTime = nil;
154+
configFlags.applicationStartTime = [NSDate date];
155+
configFlags.appStartConfigFetchDelayInSeconds = appStartConfigFetchDelay;
156+
configFlags.lastFetchStatus = FIRRemoteConfigFetchStatusNoFetchYet;
157+
158+
XCTestExpectation *expectation =
159+
[self expectationWithDescription:@"Dummy expectation to wait for the fetch delay."];
160+
dispatch_after(
161+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)((appStartConfigFetchDelay + 2) * NSEC_PER_SEC)),
162+
dispatch_get_main_queue(), ^{
163+
[configFlags update];
164+
[expectation fulfill];
165+
XCTAssertTrue(configFlags.lastFetchStatus == FIRRemoteConfigFetchStatusSuccess);
166+
});
167+
[self waitForExpectationsWithTimeout:(appStartConfigFetchDelay + 3) handler:nil];
168+
}
169+
116170
/** Validate the configuration update does not happen immediately after fetching. */
117171
- (void)testConfigUpdateDoesNotHappenImmediately {
118172
FPRFakeRemoteConfig *remoteConfig = [[FPRFakeRemoteConfig alloc] init];
@@ -123,6 +177,7 @@ - (void)testConfigUpdateDoesNotHappenImmediately {
123177
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
124178

125179
remoteConfig.fetchStatus = FIRRemoteConfigFetchStatusSuccess;
180+
configFlags.appStartConfigFetchDelayInSeconds = 0.0;
126181
[configFlags update];
127182
XCTAssertNotNil(configFlags.lastFetchedTime);
128183

@@ -146,6 +201,7 @@ - (void)testConfigUpdateHappensIfIntialFetchHasNotHappened {
146201
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
147202

148203
remoteConfig.fetchStatus = FIRRemoteConfigFetchStatusSuccess;
204+
configFlags.appStartConfigFetchDelayInSeconds = 0.0;
149205
[configFlags update];
150206
XCTAssertNotNil(configFlags.lastFetchedTime);
151207

@@ -179,18 +235,21 @@ - (void)testConfigFetchHappensAfterDelay {
179235

180236
FPRRemoteConfigFlags *configFlags =
181237
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
238+
configFlags.appStartConfigFetchDelayInSeconds = 0.0;
182239

183240
remoteConfig.fetchStatus = FIRRemoteConfigFetchStatusSuccess;
184241
XCTestExpectation *expectation =
185242
[self expectationWithDescription:@"Dummy expectation to wait for the fetch delay."];
186-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
187-
(int64_t)((kFPRConfigAppStartDelayInSeconds + 5) * NSEC_PER_SEC)),
188-
dispatch_get_main_queue(), ^{
189-
[expectation fulfill];
190-
XCTAssertNotNil(configFlags.lastFetchedTime);
191-
XCTAssertNotNil(remoteConfig.lastFetchTime);
192-
});
193-
[self waitForExpectationsWithTimeout:(kFPRConfigAppStartDelayInSeconds + 6) handler:nil];
243+
dispatch_after(
244+
dispatch_time(DISPATCH_TIME_NOW,
245+
(int64_t)((kFPRMinAppStartConfigFetchDelayInSeconds + 5) * NSEC_PER_SEC)),
246+
dispatch_get_main_queue(), ^{
247+
[configFlags update];
248+
[expectation fulfill];
249+
XCTAssertNotNil(configFlags.lastFetchedTime);
250+
XCTAssertNotNil(remoteConfig.lastFetchTime);
251+
});
252+
[self waitForExpectationsWithTimeout:(kFPRMinAppStartConfigFetchDelayInSeconds + 6) handler:nil];
194253
}
195254

196255
#pragma mark - App config related tests

FirebasePerformance/Tests/Unit/Gauges/FPRGaugeManagerTests.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ - (void)testGaugeCollectionEnabledWhenSDKFlagEnabled {
6565

6666
FPRRemoteConfigFlags *configFlags =
6767
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
68+
configFlags.appStartConfigFetchDelayInSeconds = 0.0;
6869
configurations.remoteConfigFlags = configFlags;
6970

7071
NSData *valueData = [@"false" dataUsingEncoding:NSUTF8StringEncoding];

FirebasePerformance/Tests/Unit/Instruments/FIRHTTPMetricTests.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ - (void)testMetricCreationWhenSDKFlagDisabled {
9393

9494
FPRRemoteConfigFlags *configFlags =
9595
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
96+
configFlags.appStartConfigFetchDelayInSeconds = 0.0;
9697
configurations.remoteConfigFlags = configFlags;
9798

9899
NSData *valueData = [@"false" dataUsingEncoding:NSUTF8StringEncoding];

FirebasePerformance/Tests/Unit/Timer/FIRTraceTest.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ - (void)testTraceCreationWhenSDKFlagDisabled {
7373

7474
FPRRemoteConfigFlags *configFlags =
7575
[[FPRRemoteConfigFlags alloc] initWithRemoteConfig:(FIRRemoteConfig *)remoteConfig];
76+
configFlags.appStartConfigFetchDelayInSeconds = 0.0;
7677
configurations.remoteConfigFlags = configFlags;
7778

7879
NSData *valueData = [@"false" dataUsingEncoding:NSUTF8StringEncoding];

0 commit comments

Comments
 (0)