Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions FirebaseRemoteConfig/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Unreleased
- [Added] This change introduces improvements to how the SDK handles real-time requests when a
Firebase project has exceeded its available quota for real-time services. Released in anticipation
of future quota enforcement, this change is designed to fetch the latest template even when the
quota is exhausted.

# 11.14.0
- [fixed] Fix build warning from comparison of different enumeration types.

Expand Down
4 changes: 4 additions & 0 deletions FirebaseRemoteConfig/Sources/Private/RCNConfigSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@
/// indicates a server issue.
- (void)updateRealtimeExponentialBackoffTime;

/// Increases the throttling time for Realtime. Should only be called if we receive a Realtime
/// retry interval in the response.
- (void)updateRealtimeBackoffTimeWithInterval:(NSTimeInterval)realtimeRetryInterval;

/// Update last active template version from last fetched template version.
- (void)updateLastActiveTemplateVersion;

Expand Down
13 changes: 13 additions & 0 deletions FirebaseRemoteConfig/Sources/RCNConfigRealtime.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
/// Invalidation message field names.
static NSString *const kTemplateVersionNumberKey = @"latestTemplateVersionNumber";
static NSString *const kIsFeatureDisabled = @"featureDisabled";
static NSString *const kRealtime_Retry_Interval = @"retryIntervalSeconds";

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

- (void)evaluateStreamResponse:(NSDictionary *)response error:(NSError *)dataError {
NSInteger updateTemplateVersion = 1;
NSTimeInterval realtimeRetryInterval = 0;
if (dataError == nil) {
if ([response objectForKey:kTemplateVersionNumberKey]) {
updateTemplateVersion = [[response objectForKey:kTemplateVersionNumberKey] integerValue];
}
if ([response objectForKey:kIsFeatureDisabled]) {
self->_isRealtimeDisabled = [response objectForKey:kIsFeatureDisabled];
}
if ([response objectForKey:kRealtime_Retry_Interval]) {
realtimeRetryInterval = [[response objectForKey:kRealtime_Retry_Interval] integerValue];
}

if (self->_isRealtimeDisabled) {
[self pauseRealtimeStream];
Expand All @@ -544,6 +549,14 @@ - (void)evaluateStreamResponse:(NSDictionary *)response error:(NSError *)dataErr
if (updateTemplateVersion > clientTemplateVersion) {
[self autoFetch:gFetchAttempts targetVersion:updateTemplateVersion];
}

/// This field in the response indicates that the realtime request has exceeded the project's
/// quota. It will retry after the specified interval to establish a long-lived connection.
/// This interval extends the backoff duration without affecting the number of retries, so it
/// will not enter an exponential backoff state.
if (realtimeRetryInterval > 0) {
[self->_settings updateRealtimeBackoffTimeWithInterval:realtimeRetryInterval];
}
}
} else {
NSError *error =
Expand Down
10 changes: 10 additions & 0 deletions FirebaseRemoteConfig/Sources/RCNConfigSettings.m
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,16 @@ - (void)updateRealtimeExponentialBackoffTime {
setCurrentRealtimeThrottlingRetryIntervalSeconds:_realtimeExponentialBackoffRetryInterval];
}

/// Increase the real-time stream's backoff period from the current time plus the retry interval.
/// Any subsequent Realtime requests will be checked and allowed only if past this throttle end
/// time.
- (void)updateRealtimeBackoffTimeWithInterval:(NSTimeInterval)realtimeRetryInterval {
_realtimeExponentialBackoffThrottleEndTime =
[[NSDate date] timeIntervalSince1970] + realtimeRetryInterval;

[_userDefaultsManager setRealtimeThrottleEndTime:_realtimeExponentialBackoffThrottleEndTime];
}

- (void)setRealtimeRetryCount:(int)realtimeRetryCount {
_realtimeRetryCount = realtimeRetryCount;
[_userDefaultsManager setRealtimeRetryCount:_realtimeRetryCount];
Expand Down
31 changes: 31 additions & 0 deletions FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,37 @@ - (void)testRealtimeDisabled {
}
}

- (void)testRealtimeUpdatesBackoffMetadataWhenRetryIntervalIsProvided {
NSMutableArray<XCTestExpectation *> *expectations =
[[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
expectations[i] =
[self expectationWithDescription:
[NSString stringWithFormat:@"Test backoff metadata updates with a provided retry "
@"interval in the stream response - instance %d",
i]];
NSTimeInterval realtimeRetryInterval = 240;
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setValue:@"1" forKey:@"latestTemplateVersionNumber"];
[dictionary setValue:@(realtimeRetryInterval) forKey:@"retryIntervalSeconds"];

NSTimeInterval expectedThrottleEndTime =
[[NSDate date] timeIntervalSince1970] + realtimeRetryInterval;

[_configRealtime[i] evaluateStreamResponse:dictionary error:nil];
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_checkCompletionTimeout * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSTimeInterval retrievedThrottleEndTime =
self->_configInstances[i].settings.realtimeExponentialBackoffThrottleEndTime;
XCTAssertEqualWithAccuracy(retrievedThrottleEndTime, expectedThrottleEndTime, 1.0);
[expectations[i] fulfill];
});

[self waitForExpectationsWithTimeout:_expectationTimeout handler:nil];
}
}

- (void)testRealtimeStreamRequestBody {
XCTestExpectation *requestBodyExpectation = [self expectationWithDescription:@"requestBody"];
__block NSData *requestBody;
Expand Down
Loading