Skip to content

Commit 7aefc70

Browse files
author
Alexis Oyama
committed
perf(request): Send requests in order
This is required from the server so that it can detect duplicates
1 parent 1d3fdd0 commit 7aefc70

File tree

2 files changed

+121
-111
lines changed

2 files changed

+121
-111
lines changed

Leanplum-SDK/Classes/LPNetworkOperation.m

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,6 @@ - (void)runSynchronously:(BOOL)synchronous
161161
self.response = response;
162162
self.dataFromResponse = data;
163163

164-
if (synchronous) {
165-
dispatch_semaphore_signal(sem);
166-
}
167-
168164
if (error) {
169165
if (self.errorBlock) {
170166
self.errorBlock(self, error);
@@ -175,6 +171,10 @@ - (void)runSynchronously:(BOOL)synchronous
175171
}
176172
}
177173
[self.session finishTasksAndInvalidate];
174+
175+
if (synchronous) {
176+
dispatch_semaphore_signal(sem);
177+
}
178178
};
179179

180180
// Callback on the main queue

Leanplum-SDK/Classes/LeanplumRequest.m

Lines changed: 117 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
static NSMutableDictionary *fileUploadProgress;
4646
static NSString *fileUploadProgressString;
4747
static NSMutableDictionary *pendingUploads;
48+
static NSOperationQueue *sendNowQueue;
4849

4950
static NSDictionary *_requestHheaders;
5051

@@ -139,6 +140,11 @@ - (id)initWithHttpMethod:(NSString *)httpMethod
139140
engine = [LPNetworkFactory engineWithHostName:[LPConstantsState sharedState].apiHostName
140141
customHeaderFields:_requestHheaders];
141142
}
143+
144+
if (!sendNowQueue) {
145+
sendNowQueue = [NSOperationQueue new];
146+
sendNowQueue.maxConcurrentOperationCount = 1;
147+
}
142148
}
143149
return self;
144150
}
@@ -284,126 +290,124 @@ - (void)sendNow:(BOOL)async
284290
NSLog(@"Leanplum: Cannot send request. accessKey is not set");
285291
return;
286292
}
287-
293+
288294
[self sendEventually];
289295

290-
NSArray *requestsToSend = [[LPRequestStorage sharedStorage] popAllRequests];
291-
292-
if (requestsToSend.count == 0) {
293-
return;
294-
}
295-
296-
NSString *requestData = [LeanplumRequest jsonEncodeUnsentRequests:requestsToSend];
297-
298-
LPConstantsState *constants = [LPConstantsState sharedState];
299-
NSMutableDictionary *multiRequestArgs = [NSMutableDictionary dictionaryWithObjectsAndKeys:
300-
requestData, LP_PARAM_DATA,
301-
constants.sdkVersion, LP_PARAM_SDK_VERSION,
302-
constants.client, LP_PARAM_CLIENT,
303-
LP_METHOD_MULTI, LP_PARAM_ACTION,
304-
[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]], LP_PARAM_TIME, nil];
305-
[self attachApiKeys:multiRequestArgs];
306-
int timeout = async ? constants.networkTimeoutSeconds : constants.syncNetworkTimeoutSeconds;
307-
id<LPNetworkOperationProtocol> op = [engine operationWithPath:constants.apiServlet
308-
params:multiRequestArgs
309-
httpMethod:_httpMethod
310-
ssl:constants.apiSSL
311-
timeoutSeconds:timeout];
312-
__block BOOL finished = NO;
313-
314-
// Schedule timeout.
315-
[LPTimerBlocks scheduledTimerWithTimeInterval:timeout block:^() {
316-
if (finished) {
296+
void (^operationBlock)() = ^void() {
297+
NSArray *requestsToSend = [[LPRequestStorage sharedStorage] popAllRequests];
298+
299+
if (requestsToSend.count == 0) {
317300
return;
318301
}
319-
finished = YES;
320-
LP_TRY
321-
NSLog(@"Leanplum: Request %@ timed out", _apiMethod);
322-
[op cancel];
323-
[LeanplumRequest pushUnsentRequests:requestsToSend];
324-
if (_error != nil) {
325-
_error([NSError errorWithDomain:@"Leanplum" code:1
326-
userInfo:@{NSLocalizedDescriptionKey: @"Request timed out"}]);
327-
}
328-
LP_END_TRY
329-
} repeats:NO];
330302

331-
[op addCompletionHandler:^(id<LPNetworkOperationProtocol> operation, id json) {
332-
if (finished) {
333-
return;
334-
}
335-
finished = YES;
336-
LP_TRY
303+
NSString *requestData = [LeanplumRequest jsonEncodeUnsentRequests:requestsToSend];
304+
305+
LPConstantsState *constants = [LPConstantsState sharedState];
306+
NSMutableDictionary *multiRequestArgs = [NSMutableDictionary dictionaryWithObjectsAndKeys:
307+
requestData, LP_PARAM_DATA,
308+
constants.sdkVersion, LP_PARAM_SDK_VERSION,
309+
constants.client, LP_PARAM_CLIENT,
310+
LP_METHOD_MULTI, LP_PARAM_ACTION,
311+
[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]], LP_PARAM_TIME, nil];
312+
[self attachApiKeys:multiRequestArgs];
313+
int timeout = async ? constants.networkTimeoutSeconds : constants.syncNetworkTimeoutSeconds;
314+
id<LPNetworkOperationProtocol> op = [engine operationWithPath:constants.apiServlet
315+
params:multiRequestArgs
316+
httpMethod:_httpMethod
317+
ssl:constants.apiSSL
318+
timeoutSeconds:timeout];
319+
__block BOOL finished = NO;
320+
321+
// Schedule timeout.
322+
[LPTimerBlocks scheduledTimerWithTimeInterval:timeout block:^() {
323+
if (finished) {
324+
return;
325+
}
326+
finished = YES;
327+
LP_TRY
328+
NSLog(@"Leanplum: Request %@ timed out", _apiMethod);
329+
[op cancel];
330+
[LeanplumRequest pushUnsentRequests:requestsToSend];
331+
if (_error != nil) {
332+
_error([NSError errorWithDomain:@"Leanplum" code:1
333+
userInfo:@{NSLocalizedDescriptionKey: @"Request timed out"}]);
334+
}
335+
LP_END_TRY
336+
} repeats:NO];
337337

338-
// Handle errors that don't return an HTTP error code.
339-
NSUInteger numResponses = [LPResponse numResponsesInDictionary:json];
340-
for (NSUInteger i = 0; i < numResponses; i++) {
341-
NSDictionary *response = [LPResponse getResponseAt:i fromDictionary:json];
342-
if (![LPResponse isResponseSuccess:response]) {
343-
NSString *errorMessage = [LPResponse getResponseError:response];
344-
if (!errorMessage) {
345-
errorMessage = @"API error";
346-
} else {
347-
errorMessage = [NSString stringWithFormat:@"API error: %@", errorMessage];
348-
}
349-
NSLog(@"Leanplum: %@", errorMessage);
350-
if (i == numResponses - 1) {
351-
if (_error != nil) {
352-
_error([NSError errorWithDomain:@"Leanplum" code:2
353-
userInfo:@{NSLocalizedDescriptionKey: errorMessage}]);
338+
[op addCompletionHandler:^(id<LPNetworkOperationProtocol> operation, id json) {
339+
if (finished) {
340+
return;
341+
}
342+
finished = YES;
343+
LP_TRY
344+
345+
// Handle errors that don't return an HTTP error code.
346+
NSUInteger numResponses = [LPResponse numResponsesInDictionary:json];
347+
for (NSUInteger i = 0; i < numResponses; i++) {
348+
NSDictionary *response = [LPResponse getResponseAt:i fromDictionary:json];
349+
if (![LPResponse isResponseSuccess:response]) {
350+
NSString *errorMessage = [LPResponse getResponseError:response];
351+
if (!errorMessage) {
352+
errorMessage = @"API error";
353+
} else {
354+
errorMessage = [NSString stringWithFormat:@"API error: %@", errorMessage];
355+
}
356+
NSLog(@"Leanplum: %@", errorMessage);
357+
if (i == numResponses - 1) {
358+
if (_error != nil) {
359+
_error([NSError errorWithDomain:@"Leanplum" code:2
360+
userInfo:@{NSLocalizedDescriptionKey: errorMessage}]);
361+
}
362+
return;
354363
}
355-
return;
356364
}
357365
}
358-
}
359-
LP_END_TRY
360-
if (_response != nil) {
361-
_response(operation, json);
362-
}
363-
} errorHandler:^(id<LPNetworkOperationProtocol> completedOperation, NSError *err) {
364-
if (finished) {
365-
return;
366-
}
367-
finished = YES;
368-
LP_TRY
369-
NSInteger httpStatusCode = completedOperation.HTTPStatusCode;
370-
if (httpStatusCode == 408
371-
|| (httpStatusCode >= 500 && httpStatusCode < 600)
372-
|| err.code == NSURLErrorBadServerResponse
373-
|| err.code == NSURLErrorCannotConnectToHost
374-
|| err.code == NSURLErrorDNSLookupFailed
375-
|| err.code == NSURLErrorNotConnectedToInternet
376-
|| err.code == NSURLErrorTimedOut) {
377-
NSLog(@"Leanplum: %@", err);
378-
[LeanplumRequest pushUnsentRequests:requestsToSend];
379-
} else {
380-
id errorResponse = completedOperation.responseJSON;
381-
NSString *errorMessage = [LPResponse getResponseError:[LPResponse getLastResponse:errorResponse]];
382-
if (errorMessage && [errorMessage hasPrefix:@"App not found"]) {
383-
errorMessage = @"No app matching the provided app ID was found.";
384-
constants.isInPermanentFailureState = YES;
385-
} else if (errorMessage && [errorMessage hasPrefix:@"Invalid access key"]) {
386-
errorMessage = @"The access key you provided is not valid for this app.";
387-
constants.isInPermanentFailureState = YES;
388-
} else if (errorMessage && [errorMessage hasPrefix:@"Development mode requested but not permitted"]) {
389-
errorMessage = @"A call to [Leanplum setAppIdForDevelopmentMode] with your production key was made, which is not permitted.";
390-
constants.isInPermanentFailureState = YES;
366+
LP_END_TRY
367+
if (_response != nil) {
368+
_response(operation, json);
391369
}
392-
if (errorMessage) {
393-
NSLog(@"Leanplum: %@", errorMessage);
394-
} else {
370+
} errorHandler:^(id<LPNetworkOperationProtocol> completedOperation, NSError *err) {
371+
if (finished) {
372+
return;
373+
}
374+
finished = YES;
375+
LP_TRY
376+
NSInteger httpStatusCode = completedOperation.HTTPStatusCode;
377+
if (httpStatusCode == 408
378+
|| (httpStatusCode >= 500 && httpStatusCode < 600)
379+
|| err.code == NSURLErrorBadServerResponse
380+
|| err.code == NSURLErrorCannotConnectToHost
381+
|| err.code == NSURLErrorDNSLookupFailed
382+
|| err.code == NSURLErrorNotConnectedToInternet
383+
|| err.code == NSURLErrorTimedOut) {
395384
NSLog(@"Leanplum: %@", err);
385+
[LeanplumRequest pushUnsentRequests:requestsToSend];
386+
} else {
387+
id errorResponse = completedOperation.responseJSON;
388+
NSString *errorMessage = [LPResponse getResponseError:[LPResponse getLastResponse:errorResponse]];
389+
if (errorMessage && [errorMessage hasPrefix:@"App not found"]) {
390+
errorMessage = @"No app matching the provided app ID was found.";
391+
constants.isInPermanentFailureState = YES;
392+
} else if (errorMessage && [errorMessage hasPrefix:@"Invalid access key"]) {
393+
errorMessage = @"The access key you provided is not valid for this app.";
394+
constants.isInPermanentFailureState = YES;
395+
} else if (errorMessage && [errorMessage hasPrefix:@"Development mode requested but not permitted"]) {
396+
errorMessage = @"A call to [Leanplum setAppIdForDevelopmentMode] with your production key was made, which is not permitted.";
397+
constants.isInPermanentFailureState = YES;
398+
}
399+
if (errorMessage) {
400+
NSLog(@"Leanplum: %@", errorMessage);
401+
} else {
402+
NSLog(@"Leanplum: %@", err);
403+
}
396404
}
397-
}
398-
if (_error != nil) {
399-
_error(err);
400-
}
401-
LP_END_TRY
402-
}];
405+
if (_error != nil) {
406+
_error(err);
407+
}
408+
LP_END_TRY
409+
}];
403410

404-
if (async) {
405-
[engine enqueueOperation: op];
406-
} else {
407411
// Execute synchronously. Don't block for more than 'timeout' seconds.
408412
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
409413
[engine runSynchronously:op];
@@ -412,6 +416,12 @@ - (void)sendNow:(BOOL)async
412416
while (!finished && [[NSDate date] timeIntervalSince1970] - startTime < timeout) {
413417
[NSThread sleepForTimeInterval:0.1];
414418
}
419+
};
420+
421+
if (async) {
422+
[sendNowQueue addOperationWithBlock:operationBlock];
423+
} else {
424+
operationBlock();
415425
}
416426
}
417427

0 commit comments

Comments
 (0)