Skip to content

Commit f917996

Browse files
Adjust Storage Timeouts to GTM's retry interval (#6791)
1 parent 6160676 commit f917996

File tree

9 files changed

+129
-3
lines changed

9 files changed

+129
-3
lines changed

FirebaseStorage/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
that contain the "+" sign.
66
- [changed] Renamed `list(withMaxResults:)` to `list(maxResults:)` in the Swift
77
API.
8+
- [fixed] Fixed an issue that caused longer than expected timeouts for users
9+
that specified custom timeouts.
810

911
# 3.8.1
1012
- [fixed] Fixed typo in doc comments (#6485).

FirebaseStorage/Sources/FIRStorage.m

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
@interface FIRStorage () {
4242
/// Stored Auth reference, if it exists. This needs to be stored for `copyWithZone:`.
4343
id<FIRAuthInterop> _Nullable _auth;
44+
NSTimeInterval _maxUploadRetryTime;
45+
NSTimeInterval _maxDownloadRetryTime;
46+
NSTimeInterval _maxOperationRetryTime;
4447
}
4548
@end
4649

@@ -154,8 +157,14 @@ - (instancetype)initWithApp:(FIRApp *)app
154157
_dispatchQueue = dispatch_queue_create("com.google.firebase.storage", DISPATCH_QUEUE_SERIAL);
155158
_fetcherServiceForApp = [FIRStorage fetcherServiceForApp:_app bucket:bucket auth:auth];
156159
_maxDownloadRetryTime = 600.0;
160+
_maxDownloadRetryInterval =
161+
[FIRStorageUtils computeRetryIntervalFromRetryTime:_maxDownloadRetryTime];
157162
_maxOperationRetryTime = 120.0;
163+
_maxOperationRetryInterval =
164+
[FIRStorageUtils computeRetryIntervalFromRetryTime:_maxOperationRetryTime];
158165
_maxUploadRetryTime = 600.0;
166+
_maxUploadRetryInterval =
167+
[FIRStorageUtils computeRetryIntervalFromRetryTime:_maxUploadRetryTime];
159168
}
160169
return self;
161170
}
@@ -205,6 +214,50 @@ - (NSString *)description {
205214
return [NSString stringWithFormat:@"%@ %p: %@", [self class], self, _app];
206215
}
207216

217+
#pragma mark - Retry time intervals
218+
219+
- (void)setMaxUploadRetryTime:(NSTimeInterval)maxUploadRetryTime {
220+
@synchronized(self) {
221+
_maxUploadRetryTime = maxUploadRetryTime;
222+
_maxUploadRetryInterval =
223+
[FIRStorageUtils computeRetryIntervalFromRetryTime:maxUploadRetryTime];
224+
}
225+
}
226+
227+
- (NSTimeInterval)maxDownloadRetryTime {
228+
@synchronized(self) {
229+
return _maxDownloadRetryTime;
230+
}
231+
}
232+
233+
- (void)setMaxDownloadRetryTime:(NSTimeInterval)maxDownloadRetryTime {
234+
@synchronized(self) {
235+
_maxDownloadRetryTime = maxDownloadRetryTime;
236+
_maxDownloadRetryInterval =
237+
[FIRStorageUtils computeRetryIntervalFromRetryTime:maxDownloadRetryTime];
238+
}
239+
}
240+
241+
- (NSTimeInterval)maxUploadRetryTime {
242+
@synchronized(self) {
243+
return _maxUploadRetryTime;
244+
}
245+
}
246+
247+
- (void)setMaxOperationRetryTime:(NSTimeInterval)maxOperationRetryTime {
248+
@synchronized(self) {
249+
_maxOperationRetryTime = maxOperationRetryTime;
250+
_maxOperationRetryInterval =
251+
[FIRStorageUtils computeRetryIntervalFromRetryTime:maxOperationRetryTime];
252+
}
253+
}
254+
255+
- (NSTimeInterval)maxOperationRetryTime {
256+
@synchronized(self) {
257+
return _maxOperationRetryTime;
258+
}
259+
}
260+
208261
#pragma mark - Public methods
209262

210263
- (FIRStorageReference *)reference {
@@ -261,4 +314,5 @@ + (void)enableBackgroundTasks:(BOOL)isEnabled {
261314
[NSException raise:NSGenericException format:@"getDownloadTasks not implemented"];
262315
return nil;
263316
}
317+
264318
@end

FirebaseStorage/Sources/FIRStorageDownloadTask.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#import "FirebaseStorage/Sources/FIRStorageDownloadTask_Private.h"
1919
#import "FirebaseStorage/Sources/FIRStorageObservableTask_Private.h"
2020
#import "FirebaseStorage/Sources/FIRStorageTask_Private.h"
21+
#import "FirebaseStorage/Sources/FIRStorage_Private.h"
2122

2223
@implementation FIRStorageDownloadTask
2324

@@ -80,7 +81,7 @@ - (void)enqueueWithData:(nullable NSData *)resumeData {
8081
}
8182
}];
8283

83-
fetcher.maxRetryInterval = strongSelf.reference.storage.maxDownloadRetryTime;
84+
fetcher.maxRetryInterval = strongSelf.reference.storage.maxDownloadRetryInterval;
8485

8586
if (strongSelf->_fileURL) {
8687
// Handle file downloads

FirebaseStorage/Sources/FIRStorageTask.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ - (instancetype)initWithReference:(FIRStorageReference *)reference
4545
_reference = reference;
4646
_baseRequest = [FIRStorageUtils defaultRequestForPath:reference.path];
4747
_fetcherService = service;
48-
_fetcherService.maxRetryInterval = _reference.storage.maxOperationRetryTime;
48+
_fetcherService.maxRetryInterval = _reference.storage.maxOperationRetryInterval;
4949
_dispatchQueue = queue;
5050
}
5151
return self;

FirebaseStorage/Sources/FIRStorageUploadTask.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#import "FirebaseStorage/Sources/FIRStorageObservableTask_Private.h"
2020
#import "FirebaseStorage/Sources/FIRStorageTask_Private.h"
2121
#import "FirebaseStorage/Sources/FIRStorageUploadTask_Private.h"
22+
#import "FirebaseStorage/Sources/FIRStorage_Private.h"
2223

2324
#if SWIFT_PACKAGE
2425
@import GTMSessionFetcherCore;
@@ -128,7 +129,7 @@ - (void)enqueue {
128129
uploadFetcher.comment = @"File UploadTask";
129130
}
130131

131-
uploadFetcher.maxRetryInterval = self.reference.storage.maxUploadRetryTime;
132+
uploadFetcher.maxRetryInterval = self.reference.storage.maxUploadRetryInterval;
132133

133134
[uploadFetcher setSendProgressBlock:^(int64_t bytesSent, int64_t totalBytesSent,
134135
int64_t totalBytesExpectedToSend) {

FirebaseStorage/Sources/FIRStorageUtils.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ NS_ASSUME_NONNULL_BEGIN
8484
*/
8585
+ (NSError *)storageErrorWithDescription:(NSString *)description code:(NSInteger)code;
8686

87+
/**
88+
* Performs a crude translation of the user provided timeouts to the retry intervals that
89+
* GTMSessionFetcher accepts. GTMSessionFetcher times out operations if the time between individual
90+
* retry attempts exceed a certain threshold, while our API contract looks at the total observed
91+
* time of the operation (i.e. the sum of all retries).
92+
* @param retryTime A timeout that caps the sum of all retry attempts
93+
* @return A timeout that caps the timeout of the last retry attempt
94+
*/
95+
+ (NSTimeInterval)computeRetryIntervalFromRetryTime:(NSTimeInterval)retryTime;
96+
8797
@end
8898

8999
@interface NSDictionary (FIRStorageNSDictionaryJSONHelpers)

FirebaseStorage/Sources/FIRStorageUtils.m

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,23 @@ + (NSError *)storageErrorWithDescription:(NSString *)description code:(NSInteger
134134
userInfo:@{NSLocalizedDescriptionKey : description}];
135135
}
136136

137+
+ (NSTimeInterval)computeRetryIntervalFromRetryTime:(NSTimeInterval)retryTime {
138+
// GTMSessionFetcher's retry starts at 1 second and then doubles every time. We use this
139+
// information to compute a best-effort estimate of what to translate the user provided retry
140+
// time into.
141+
142+
// Note that this is the same as 2 << (log2(retryTime) - 1), but deemed more readable.
143+
NSTimeInterval lastInterval = 1.0;
144+
NSTimeInterval sumOfAllIntervals = 1.0;
145+
146+
while (sumOfAllIntervals < retryTime) {
147+
lastInterval *= 2;
148+
sumOfAllIntervals += lastInterval;
149+
}
150+
151+
return lastInterval;
152+
}
153+
137154
@end
138155

139156
@implementation NSDictionary (FIRStorageNSDictionaryJSONHelpers)

FirebaseStorage/Sources/FIRStorage_Private.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,27 @@ NS_ASSUME_NONNULL_BEGIN
2929

3030
@property(strong, nonatomic) NSString *storageBucket;
3131

32+
/**
33+
* Maximum time between retry attempts for uploads.
34+
*
35+
* This is used by GTMSessionFetcher and translated from the user provided `maxUploadRetryTime`.
36+
*/
37+
@property(assign, nonatomic) NSTimeInterval maxUploadRetryInterval;
38+
39+
/**
40+
* Maximum time between retry attempts for downloads.
41+
*
42+
* This is used by GTMSessionFetcher and translated from the user provided `maxDownloadRetryTime`.
43+
*/
44+
@property(assign, nonatomic) NSTimeInterval maxDownloadRetryInterval;
45+
46+
/**
47+
* Maximum time between retry attempts for any operation that is not an upload or download.
48+
*
49+
* This is used by GTMSessionFetcher and translated from the user provided `maxOperationRetryTime`.
50+
*/
51+
@property(assign, nonatomic) NSTimeInterval maxOperationRetryInterval;
52+
3253
/**
3354
* Enables/disables GTMSessionFetcher HTTP logging
3455
* @param isLoggingEnabled Boolean passed through to enable/disable GTMSessionFetcher logging

FirebaseStorage/Tests/Unit/FIRStorageUtilsTests.m

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,24 @@ - (void)testEncodedURLForNoPath {
122122
XCTAssertEqualObjects(encodedURL, @"/v0/b/bucket/o");
123123
}
124124

125+
- (void)testTranslateRetryTime {
126+
// The 1st retry attempt runs after 1 second.
127+
// The 2nd retry attempt is delayed by 2 seconds (3s total)
128+
// The 3rd retry attempt is delayed by 4 seconds (7s total)
129+
// The 4th retry attempt is delayed by 8 seconds (15s total)
130+
// The 5th retry attempt is delayed by 16 seconds (31s total)
131+
// The 6th retry attempt is delayed by 32 seconds (63s total)
132+
// Thus, we should exit just between the 5th and 6th retry attempt and cut off before 32s.
133+
XCTAssertEqual(32.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:60.0]);
134+
135+
XCTAssertEqual(1.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:1.0]);
136+
XCTAssertEqual(2.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:2.0]);
137+
XCTAssertEqual(4.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:4.0]);
138+
XCTAssertEqual(8.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:10.0]);
139+
XCTAssertEqual(16.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:20.0]);
140+
XCTAssertEqual(16.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:30.0]);
141+
XCTAssertEqual(32.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:40.0]);
142+
XCTAssertEqual(32.0, [FIRStorageUtils computeRetryIntervalFromRetryTime:50.0]);
143+
}
144+
125145
@end

0 commit comments

Comments
 (0)