Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create HTTP requests on a background queue. #720

Merged
merged 1 commit into from
Sep 28, 2017
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion Analytics/Classes/Internal/SEGHTTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
* NOTE: You need to re-dispatch within the completionHandler onto a desired queue to avoid threading issues.
* Completion handlers are called on a dispatch queue internal to SEGHTTPClient.
*/
- (NSURLSessionUploadTask *)upload:(JSON_DICT)batch forWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL retry))completionHandler;
- (NSURLSessionUploadTask *)upload:(NSArray *)batch forWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL retry))completionHandler;

- (NSURLSessionDataTask *)settingsForWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL success, JSON_DICT _Nullable settings))completionHandler;

Expand Down
13 changes: 9 additions & 4 deletions Analytics/Classes/Internal/SEGHTTPClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ - (void)dealloc
}


- (NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL retry))completionHandler
- (NSURLSessionUploadTask *)upload:(NSArray *)batch forWriteKey:(NSString *)writeKey completionHandler:(void (^)(BOOL retry))completionHandler
{
// batch = SEGCoerceDictionary(batch);
NSURLSession *session = [self sessionForWriteKey:writeKey];
Expand All @@ -75,11 +75,16 @@ - (NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(NSString *

[request setHTTPMethod:@"POST"];

// In particular, set the sentAt after the requestFactory is invoked so that it can be as up to date as possible.
NSMutableDictionary *payload = [[NSMutableDictionary alloc] init];
[payload setObject:iso8601FormattedString([NSDate date]) forKey:@"sentAt"];
[payload setObject:batch forKey:@"batch"];

NSError *error = nil;
NSException *exception = nil;
NSData *payload = nil;
NSData *payloadData = nil;
@try {
payload = [NSJSONSerialization dataWithJSONObject:batch options:0 error:&error];
payloadData = [NSJSONSerialization dataWithJSONObject:batch options:0 error:&error];
}
@catch (NSException *exc) {
exception = exc;
Expand All @@ -89,7 +94,7 @@ - (NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(NSString *
completionHandler(NO); // Don't retry this batch.
return nil;
}
NSData *gzippedPayload = [payload seg_gzippedData];
NSData *gzippedPayload = [payloadData seg_gzippedData];

NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:gzippedPayload completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
if (error) {
Expand Down
55 changes: 32 additions & 23 deletions Analytics/Classes/Internal/SEGSegmentIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ @interface SEGSegmentIntegration ()
@property (nonatomic, strong) NSMutableArray *queue;
@property (nonatomic, strong) NSDictionary *cachedStaticContext;
@property (nonatomic, strong) NSURLSessionUploadTask *batchRequest;
@property (nonatomic, assign) BOOL batchRequestInProgress; // Thread confined to serialQueue.
@property (nonatomic, assign) UIBackgroundTaskIdentifier flushTaskID;
@property (nonatomic, strong) SEGReachability *reachability;
@property (nonatomic, strong) NSTimer *flushTimer;
@property (nonatomic, strong) dispatch_queue_t serialQueue;
@property (nonatomic, strong) dispatch_queue_t networkQueue;
@property (nonatomic, strong) dispatch_queue_t backgroundTaskQueue;
@property (nonatomic, strong) NSMutableDictionary *traits;
@property (nonatomic, assign) SEGAnalytics *analytics;
Expand Down Expand Up @@ -87,8 +89,9 @@ - (id)initWithAnalytics:(SEGAnalytics *)analytics httpClient:(SEGHTTPClient *)ht
self.reachability = [SEGReachability reachabilityWithHostname:@"google.com"];
[self.reachability startNotifier];
self.cachedStaticContext = [self staticContext];
self.serialQueue = seg_dispatch_queue_create_specific("io.segment.analytics.segmentio", DISPATCH_QUEUE_SERIAL);
self.backgroundTaskQueue = seg_dispatch_queue_create_specific("io.segment.analytics.backgroundTask", DISPATCH_QUEUE_SERIAL);
self.serialQueue = seg_dispatch_queue_create_specific("com.segment.analytics.segment", DISPATCH_QUEUE_SERIAL);
self.networkQueue = seg_dispatch_queue_create_specific("com.segment.analytics.segment.network", DISPATCH_QUEUE_SERIAL);
self.backgroundTaskQueue = seg_dispatch_queue_create_specific("com.segment.analytics.segment.backgroundTask", DISPATCH_QUEUE_SERIAL);
self.flushTaskID = UIBackgroundTaskInvalid;

#if !TARGET_OS_TV
Expand Down Expand Up @@ -487,7 +490,7 @@ - (void)flushWithMaxSize:(NSUInteger)maxBatchSize
[self endBackgroundTask];
return;
}
if (self.batchRequest != nil) {
if (self.batchRequestInProgress) {
SEGLog(@"%@ API request already in progress, not flushing again.", self);
return;
}
Expand All @@ -508,7 +511,7 @@ - (void)flushQueueByLength
[self dispatchBackground:^{
SEGLog(@"%@ Length is %lu.", self, (unsigned long)self.queue.count);

if (self.batchRequest == nil && [self.queue count] >= self.configuration.flushAt) {
if (!self.batchRequestInProgress && [self.queue count] >= self.configuration.flushAt) {
[self flush];
}
}];
Expand Down Expand Up @@ -540,31 +543,37 @@ - (void)notifyForName:(NSString *)name userInfo:(id)userInfo

- (void)sendData:(NSArray *)batch
{
NSMutableDictionary *payload = [[NSMutableDictionary alloc] init];
[payload setObject:iso8601FormattedString([NSDate date]) forKey:@"sentAt"];
[payload setObject:batch forKey:@"batch"];
if (self.batchRequestInProgress) {
SEGLog(@"API request already in progress, not flushing again.");
return;
}

SEGLog(@"%@ Flushing %lu of %lu queued API calls.", self, (unsigned long)batch.count, (unsigned long)self.queue.count);
SEGLog(@"Flushing batch %@.", payload);
self.batchRequestInProgress = true;

self.batchRequest = [self.httpClient upload:payload forWriteKey:self.configuration.writeKey completionHandler:^(BOOL retry) {
[self dispatchBackground:^{
if (retry) {
[self notifyForName:SEGSegmentRequestDidFailNotification userInfo:batch];
seg_dispatch_specific_async(self.networkQueue, ^{
SEGLog(@"%@ Flushing %lu of %lu queued API calls.", self, (unsigned long)batch.count, (unsigned long)self.queue.count);

self.batchRequest = [self.httpClient upload:batch forWriteKey:self.configuration.writeKey completionHandler:^(BOOL retry) {
[self dispatchBackground:^{
self.batchRequestInProgress = false;

if (retry) {
[self notifyForName:SEGSegmentRequestDidFailNotification userInfo:batch];
self.batchRequest = nil;
[self endBackgroundTask];
return;
}

[self.queue removeObjectsInArray:batch];
[self persistQueue];
[self notifyForName:SEGSegmentRequestDidSucceedNotification userInfo:batch];
self.batchRequest = nil;
[self endBackgroundTask];
return;
}

[self.queue removeObjectsInArray:batch];
[self persistQueue];
[self notifyForName:SEGSegmentRequestDidSucceedNotification userInfo:batch];
self.batchRequest = nil;
[self endBackgroundTask];
}];
}];
}];

[self notifyForName:SEGSegmentDidSendRequestNotification userInfo:batch];
[self notifyForName:SEGSegmentDidSendRequestNotification userInfo:batch];
});
}

- (void)applicationDidEnterBackground
Expand Down
9 changes: 3 additions & 6 deletions AnalyticsTests/HTTPClientTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,8 @@ class HTTPClientTest: QuickSpec {

describe("upload") {
it("does not ask to retry for json error") {
let batch: [String: Any] = [
// Dates cannot be serialized as is so the json serialzation will fail.
"sentAt": NSDate(),
"batch": [["type": "track", "event": "foo"]],
]
// Dates cannot be serialized as is so the json serialzation will fail.
let batch = [[ "foo" : NSDate() ] ]
var done = false
let task = client.upload(batch, forWriteKey: "bar") { retry in
expect(retry) == false
Expand All @@ -109,7 +106,7 @@ class HTTPClientTest: QuickSpec {
expect(done).toEventually(beTrue())
}

let batch: [String: Any] = ["sentAt":"2016-07-19'T'19:25:06Z", "batch":[["type":"track", "event":"foo"]]]
let batch = [["type":"track", "event":"foo"]]


it("does not ask to retry for 2xx response") {
Expand Down