The problem
I am using google-api-objectivec-client library to work with Google Drive API.
I am creating 10 folders inside appDataFolder
(for example) without any delays and it is too fast and I am constantly getting errors for 9/10 of my requests:
Error Domain=com.google.GTLJSONRPCErrorDomain Code=500 "(Internal Error)" UserInfo={error=Internal Error, NSLocalizedFailureReason=(Internal Error)
some times these:
Error Domain=com.google.GTLJSONRPCErrorDomain Code=403 "(User Rate Limit Exceeded)" UserInfo={error=User Rate Limit Exceeded, NSLocalizedFailureReason=(User Rate Limit Exceeded),
Most likely, it is related to "spamming" or userRateLimit exceptions, because it easily can be fixed by "sleeps" (have reproduced this many times).
Suggested solution
Google suggest to retry request with such kind of errors, even it provides great API to do it automatically. According to documentation:
GTL service classes and the GTMSessionFetcher class provide a mechanism for automatic retry of a few common network and server errors, with appropriate increasing delays between each attempt. You can turn on the automatic retry support for a GTL service by setting the retryEnabled property.
// Turn on automatic retry of some common error results service.retryEnabled = YES;
The default errors retried are http status 408 (request timeout), 503 (service unavailable), and 504 (gateway timeout), NSURLErrorNetworkConnectionLost, and NSURLErrorTimedOut. You may specify a maximum retry interval other than the default of 1 minute, and can provide an optional retry selector to customize the criteria for each retry attempt.
But error with status 500 is not in default list. So, I tried to use this:
// Retry selector is optional for retries.
// If present, it should have the signature:
// -(BOOL)ticket:(GTLServiceTicket *)ticket willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error
// and return YES to cause a retry. Note that unlike the fetcher retry
// selector, this selector's first argument is a ticket, not a fetcher.
GTLService.retrySelector
I have created a method which constantly returns YES
, but it never get called and as a result request with errors are not retried.
Question
How to use automatic retry for google-api-objectivec-client
?
Update. Code sample to reproduce the issue:
- (void) reproduceTheIssue: (GTMOAuth2Authentication *) authorizer {
GTLServiceDrive * driveService = [[GTLServiceDrive alloc] init];
driveService.retryEnabled = true;
driveService.authorizer = authorizer;
// driveService.fetcherService.retryEnabled = YES; //this also doesn't help
// driveService.retrySelector = @selector(ticket:willRetry:forError:);
const int DELAY = 0;
const int NUMBER_OF_FOLDERS = 50;
for (int i = 0; i < NUMBER_OF_FOLDERS; i++) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, DELAY * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
GTLDriveFile * nestedFolder = [GTLDriveFile object];
NSString * folderName = [NSString stringWithFormat: @"folder-number-%i", i];
nestedFolder.name = folderName;
nestedFolder.mimeType = @"application/vnd.google-apps.folder";
nestedFolder.parents = @[@"appDataFolder"];
GTLQueryDrive * query = [GTLQueryDrive queryForFilesCreateWithObject: nestedFolder uploadParameters: nil];
[driveService executeQuery: query completionHandler: ^(GTLServiceTicket * ticket, GTLDriveFile * updatedFile, NSError * error) {
if (error == nil) {
NSLog(@"Successfully created a folder %@", updatedFile);
} else {
NSLog(@"Failed to a folder: %@ error: %@", folderName, error);
}
}];
});
}
}
- (BOOL) ticket: (GTLServiceTicket *) ticket willRetry: (BOOL) suggestedWillRetry forError: (NSError *) error {
NSLog(@"I will never be called :( with this arguments ticket: %@, willRetry: %i, error :%@", ticket, suggestedWillRetry, error);
return YES;
}
Update Update The issue was discussed at library's GitHub. Short answer: it is not possible to retry these error automatically. It should be managed programmatically by user in handlers
.
The retry logic is in the http fetcher class, so the retries only reflect errors that show up during network requests.