How to resume time out operations NSOperationQueue in iOS?
Asked Answered
M

3

6

I have already implemented NSOperationQueue successfully in application.

I have one operation queue which might have 1000 of NSOperations like below.

@interface Operations : NSOperation

@end

@implementation Operations

- (void)main
{
    NSURL *url = [NSURL URLWithString:@"Your URL Here"];

    NSString *contentType = @"application/json";
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request addValue:contentType forHTTPHeaderField: @"Content-Type"];
    NSError *err = nil;

    NSData *body = [NSJSONSerialization dataWithJSONObject:postVars options:NSJSONWritingPrettyPrinted error:&err];

    [request setHTTPBody:body];
    [request addValue:[NSString stringWithFormat:@"%lu", (unsigned long)body.length] forHTTPHeaderField: @"Content-Length"];

     [request setTimeoutInterval:60];

     NSHTTPURLResponse *response = nil;
     NSError *error = nil;

     NSData *resData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}

@end

Now for that queue I am adding all 1000 operations at a time. I add operation like below.

Operations *operation = [[Operations alloc]init];
[downloadQueue addOperation:operation];

Now what happens time interval is 60 as [request setTimeoutInterval:60]

So think like after 60 seconds if 300 operations out of 1000 operations is finished then other 700 operations are throwing request time out error.

So what should I do in this case.

Can I resume failed operations? Or I should again make operation and add it in queue.

Is there any better mechanism than this one?

Mcdonnell answered 27/6, 2016 at 11:33 Comment(0)
P
3

My initial inclination would be, yes, just create new operations for the timed out requests and toss 'em back into the queue. It just feels simpler since you already have logic for adding those operations for your requests. HOWEVER:

  1. Be careful to not get into sort of an infinite loop. If just ONE of those fails indefinitely for whatever reason, your queue will keep on chugging. I'd keep a failure count so that you know to stop retrying a request after some finite number of attempts.

  2. If you KNOW that a large number will always fail, consider batching them in some fashion. One option would be a chain of dependencies. eg. You could try adding 200 of your ops, a "wait" NSOperation, the next 200, another "wait" NSOperation, another "wait" op, etc. Make the first wait op be dependent on those first 200 requests. Then make the next 200 depend on that first wait op, and so on:

    • [batch 1: first 200 requests]
    • [wait 1: op that waits for all of batch 1]
    • [batch 2: next 200, each waits for wait 1]
    • [wait 2: op that waits for all of batch 2]
    • [batch 3: next 200, each waits for wait 2]
    • etc.
  3. Basically like 2 but instead of a "wait" op, have a "done" op. Lets say you have an array of 1000 requests to send: Toss 200 into the queue, then a "done" op which depends on those 200. When the "done" op runs (by definition, AFTER those 200 are done), it can then pull the next 200 from the array and toss them in (plus a new "done" op).

(maybe even consider making the wait/done op "pause" a few seconds to give the server a breather)

In other words, with #2 and #3, I'm saying "blast 200 at once, wait, then blast the next 200, wait, etc." (200 is arbitrary on my part, you know better than I as to what the best number is for your situation)

Paletot answered 6/7, 2016 at 2:15 Comment(0)
A
2

You can implement the NSURLConnectionDelegate method connection:didFailWithError: to check if the synchronous request failed, and if the error was that the request timed out. Then retry the connection in the delegate callback.

I should note that Apple strongly encourages use of NSURLSession over NSURLConnection, as many methods in NSURLConnection are now deprecated.

Acridine answered 30/6, 2016 at 13:7 Comment(2)
Can you please provide some reference which I should follow?Mcdonnell
@JayeshSojitra sure: NSURLConnectionDelegate documentation, NSURLConnection to NSURLSession Migration GuideAcridine
F
2

The first point is - don't let them all run at the same time! You can easily flood a mobile data connection if you try to run even 10 requests at the same time. So, before anything else, set the maxConcurrentOperationCount of the queue to something like 5.

This alone will likely massively reduce your issue and you might not need to do anything else.

You should also really look at using NSURLSession, and in this way you could also look at removing the operation queue and using HTTPMaximumConnectionsPerHost to control how many requests are made at the same time.

If you do still get a failure then one of the 'recursive type' options as other answers discuss is a workable solution. I'd add an attempt count to the operation subclass to make it easy to track and if it fails then check how many times it's failed and decide whether to create a new copy of the operation and add it to the queue or to raise an alert.

Fortuitism answered 6/7, 2016 at 10:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.