Skip to content

Commit eb5786d

Browse files
Improve Real-time response Handling for Remote Config (#15031)
Co-authored-by: Nick Cooke <[email protected]>
1 parent a9503fd commit eb5786d

File tree

5 files changed

+64
-0
lines changed

5 files changed

+64
-0
lines changed

FirebaseRemoteConfig/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Unreleased
2+
- [added] Improved how the SDK handles real-time requests when a Firebase
3+
project has exceeded its available quota for real-time services.
4+
Released in anticipation of future quota enforcement, this change is
5+
designed to fetch the latest template even when the quota is exhausted.
6+
17
# 11.14.0
28
- [fixed] Fix build warning from comparison of different enumeration types.
39

FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@
135135
/// indicates a server issue.
136136
- (void)updateRealtimeExponentialBackoffTime;
137137

138+
/// Increases the throttling time for Realtime. Should only be called if we receive a Realtime
139+
/// retry interval in the response.
140+
- (void)updateRealtimeBackoffTimeWithInterval:(NSTimeInterval)realtimeRetryInterval;
141+
138142
/// Update last active template version from last fetched template version.
139143
- (void)updateLastActiveTemplateVersion;
140144

FirebaseRemoteConfig/Sources/RCNConfigRealtime.m

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
/// Invalidation message field names.
6060
static NSString *const kTemplateVersionNumberKey = @"latestTemplateVersionNumber";
6161
static NSString *const kIsFeatureDisabled = @"featureDisabled";
62+
static NSString *const kRealtime_Retry_Interval = @"retryIntervalSeconds";
6263

6364
static NSTimeInterval gTimeoutSeconds = 330;
6465
static NSInteger const gFetchAttempts = 3;
@@ -521,13 +522,17 @@ - (void)autoFetch:(NSInteger)remainingAttempts targetVersion:(NSInteger)targetVe
521522

522523
- (void)evaluateStreamResponse:(NSDictionary *)response error:(NSError *)dataError {
523524
NSInteger updateTemplateVersion = 1;
525+
NSTimeInterval realtimeRetryInterval = 0;
524526
if (dataError == nil) {
525527
if ([response objectForKey:kTemplateVersionNumberKey]) {
526528
updateTemplateVersion = [[response objectForKey:kTemplateVersionNumberKey] integerValue];
527529
}
528530
if ([response objectForKey:kIsFeatureDisabled]) {
529531
self->_isRealtimeDisabled = [response objectForKey:kIsFeatureDisabled];
530532
}
533+
if ([response objectForKey:kRealtime_Retry_Interval]) {
534+
realtimeRetryInterval = [[response objectForKey:kRealtime_Retry_Interval] integerValue];
535+
}
531536

532537
if (self->_isRealtimeDisabled) {
533538
[self pauseRealtimeStream];
@@ -544,6 +549,14 @@ - (void)evaluateStreamResponse:(NSDictionary *)response error:(NSError *)dataErr
544549
if (updateTemplateVersion > clientTemplateVersion) {
545550
[self autoFetch:gFetchAttempts targetVersion:updateTemplateVersion];
546551
}
552+
553+
/// This field in the response indicates that the realtime request should retry after the
554+
/// specified interval to establish a long-lived connection. This interval extends the backoff
555+
/// duration without affecting the number of retries, so it will not enter an exponential
556+
/// backoff state.
557+
if (realtimeRetryInterval > 0) {
558+
[self->_settings updateRealtimeBackoffTimeWithInterval:realtimeRetryInterval];
559+
}
547560
}
548561
} else {
549562
NSError *error =

FirebaseRemoteConfig/Sources/RCNConfigSettings.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,16 @@ - (void)updateRealtimeExponentialBackoffTime {
234234
setCurrentRealtimeThrottlingRetryIntervalSeconds:_realtimeExponentialBackoffRetryInterval];
235235
}
236236

237+
/// Increase the real-time stream's backoff period from the current time plus the retry interval.
238+
/// Any subsequent Realtime requests will be checked and allowed only if past this throttle end
239+
/// time.
240+
- (void)updateRealtimeBackoffTimeWithInterval:(NSTimeInterval)realtimeRetryInterval {
241+
_realtimeExponentialBackoffThrottleEndTime =
242+
[[NSDate date] timeIntervalSince1970] + realtimeRetryInterval;
243+
244+
[_userDefaultsManager setRealtimeThrottleEndTime:_realtimeExponentialBackoffThrottleEndTime];
245+
}
246+
237247
- (void)setRealtimeRetryCount:(int)realtimeRetryCount {
238248
_realtimeRetryCount = realtimeRetryCount;
239249
[_userDefaultsManager setRealtimeRetryCount:_realtimeRetryCount];

FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,6 +1785,37 @@ - (void)testRealtimeDisabled {
17851785
}
17861786
}
17871787

1788+
- (void)testRealtimeUpdatesBackoffMetadataWhenRetryIntervalIsProvided {
1789+
NSMutableArray<XCTestExpectation *> *expectations =
1790+
[[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
1791+
for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
1792+
expectations[i] =
1793+
[self expectationWithDescription:
1794+
[NSString stringWithFormat:@"Test backoff metadata updates with a provided retry "
1795+
@"interval in the stream response - instance %d",
1796+
i]];
1797+
NSTimeInterval realtimeRetryInterval = 240;
1798+
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
1799+
[dictionary setValue:@"1" forKey:@"latestTemplateVersionNumber"];
1800+
[dictionary setValue:@(realtimeRetryInterval) forKey:@"retryIntervalSeconds"];
1801+
1802+
NSTimeInterval expectedThrottleEndTime =
1803+
[[NSDate date] timeIntervalSince1970] + realtimeRetryInterval;
1804+
1805+
[_configRealtime[i] evaluateStreamResponse:dictionary error:nil];
1806+
dispatch_after(
1807+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
1808+
dispatch_get_main_queue(), ^{
1809+
NSTimeInterval retrievedThrottleEndTime =
1810+
self->_configInstances[i].settings.realtimeExponentialBackoffThrottleEndTime;
1811+
XCTAssertEqualWithAccuracy(retrievedThrottleEndTime, expectedThrottleEndTime, 1.0);
1812+
[expectations[i] fulfill];
1813+
});
1814+
1815+
[self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
1816+
}
1817+
}
1818+
17881819
- (void)testRealtimeStreamRequestBody {
17891820
XCTestExpectation *requestBodyExpectation = [self expectationWithDescription:@"requestBody"];
17901821
__block NSData *requestBody;

0 commit comments

Comments
 (0)