Afnetworking: AFHTTPClient stalling when flooded with requests

Created on 27 Apr 2013  Â·  43Comments  Â·  Source: AFNetworking/AFNetworking

Opening discussion on what I think might be an issue in either AFHTTPClient or its using of NSOperationQueue. Happy to be proven wrong though.

The short version is that if you send a bunch of requests to an AFHTTPClient (+100) then the processing of those requests will appear to stall for an indeterminate amount of time. Webserver logs show that the requests has not yet reached the server. Eventually, assuming they don't timeout, the requests will be sent and processed accordingly.

Changing the maxConcurrentOperationCount of AFHTTPClient's operationQueue to something other than its default of NSOperationQueueDefaultMaxConcurrentOperationCount appears to solve the problem. All requests are processed in a timely manner.

For those interested, I've posted a test application that (at least on my machine) reproduces the problem every time:

https://github.com/kennycarruthers/afspeedtest

I'd be curious to know if others see the same behavior though I'm equally happy to be proven wrong that there's an issue somewhere else that I overlooked.

Thanks in advance to Matt, Scott and the rest of the AFNetworking community for the great library.

Most helpful comment

Apple's already answered this in one of the previous WWDC talks. NSOperationQueueDefaultMaxConcurrentOperationCount is not the right value for network- or disk-bound queues; it's strictly for CPU-bound.

All 43 comments

From documentation for NSOperationQueue

setMaxConcurrentOperationCount:
Sets the maximum number of concurrent operations that the receiver can execute.
The default maximum number of operations is determined dynamically by the NSOperationQueue object based on current system conditions.

NSOperationQueue will execute (at the same time) the number of concurrent operations up to the maxConcurrentOperationCount value. Higher value for this parameter can negatively affect on the overall performance of the application.

One would think that the system wouldn't choose a default operation count so high as to impact system performance. I would assume the whole point of setting the queue count to a default value is to explicitly avoid that condition as NSOperationQueue and its internals would have a better understand of what the maximum count should be compared to an application's idea of what it should be.

In other words, I can understand there being degraded performance if an application specifically requested an operation count of 100, but I would not expect such degraded performance if I handed over the responsibility of determining the operation count to the queue itself.

In my small test case with AFNetworking, explicitly setting the operation count at something small, like 2, improves performance for me, while letting the system determine the operation count causes significant slowdown when flooded with requests.

NSOperationQueueDefaultMaxConcurrentOperationCount, the default for an NSOperationQueue, means iOS/OSX determines how many concurrent operations are executed at once automatically. However, its algorithm only considers the CPU. It will make errors when the tasks are disk or network bound instead of CPU bound. (This is, of course, not guaranteed behaviour; theoretically, the next version of iOS/OSX could take other things into account, too.)

There was a WWDC talk on this, but I forget which one it was. Probably in 2011.

@kennycarruthers It is quite easy to tank your application's performance with NSOperation and NSOperationQueue with large queues. There are two main problems I've seen before:

  1. For queues of ~80-100 items that can all be started at once, the app can wind up exhausting all available resources. Typically when this happens it seems as though nothing is happening and pausing the app in the debugger shows tons of threads owned by GCD with nobody making any progress. The best thing to do here is either put a cap on the operation queue or use dependencies to reduce the number of operations being started at once.
  2. You can also get into trouble if you use operations with very low granularity and run a very large queue. I've had problems with a search indexer that was minting NSBlockOperation instances to index particular textual fields on objects. During a large data import task, the queue could wind up containing 20-40k+ items. Even with a concurrency limit on the queue, it effectively ruined the performance because the operations and queue had to do a ton of overhead work to resolve dependencies, order by priority, etc. In these cases, starting the operation queue would just sit for a very long time and then finally begin working to completion. Using a batching strategy to increase the amount of work per operation and decrease the overall operation count completely flipped the performance profile from unbearably slow to nice and fast.

Why are you trying to send so many requests at once?

You know, I think AFHTTPClient defaulting to NSOperationQueueDefaultMaxConcurrentOperationCount might be a bug in itself.

@tewha I agree. What do you think would be a reasonable default value, then?

@tewha I have written more than 5 fairly large applications using AFNetworking and trust me - NSOperationQueue with the default parameter is more than enough.
You can configure an AFHTTPClient instance as you wish. It is very flexible library.

@blakewatters Prior to stumbling across this issue, I had not experienced performance problems using NSOperation Queue so it was unclear to me at the time if this was something specific to AFNetworking or something more generally at fault with NSOperationQueue. That you could potentially starve some system resources, especially network related resources, by flooding a queue is somewhat understandable of CPU resources are the primary criteria for the maximum operation count.

As has been noted, I'm perfectly happy to manually set the maximum operation count in my AFHTTPClient to something that makes sense for my application. That appears to resolve the original issue I first noticed.

While not exactly what I'm working on, one example where this has come up is requesting profile information for a large number of connections in a social network. A single concurrent operation is created who's job is to fetch profile information for hundreds (or possibly thousands) of social connections for a user. If all of those requests are sent to an AFHTTPClient at the same time, the performance will suffer. If the AFHTTPClient's maximum count is set to something small like 2-4, then the network requests are handled as expected in a timely fashion.

(And for those concerned, this is not being used for scraping. Think more along the lines of "Show me the most recent piece of information for each of my social connections." )

Thanks again to all for the discussion.

Oh, there's lots of good reasons to use many requests. An API that specifies a list of resources, for instance. Based on that list of resources, I need to fetch them.

I currently limit this to 1 in my app for historical reasons, which is why I haven't run into this. I think 2-4 is probably pretty reasonable. There's probably a WWDC talk on this. If I find it, I'll let you know. :)

Operations are dummy listeners waiting for connection to be executed in the (dedicated) runloop, no matter how many concurrent operation you set, they all get executed on the run loop (single threaded). This is how connection behave in iOS. And AFNetworking (with a history of MVCNetworking) are design to give networking it's own dedicated thread. Operation queue only controls how many connections are opened.

You are suppose to categorize your network operations. In MVCNetworking, fast network operation have unlimited queue, while download operation have a max of 2 queue and also a reduce in priority I think. (Need to double check correct me if I'm wrong)

Your bottomneck can be the run loop. If you new raw threading power, you might need to run your own dedicated run loop and schedule your operation there which might need some digging at AFNetworking.

I think how many queue needed is a design choice and this AFNetworking default value is alright for me.

The comment written in MVCNetworking by Quinn the Eskimo best describe my above comment though. It's worth digging the source.

The number of simultaneous operations in the queue should be set by the developer to any value she thinks is appropriate for her use case. This effectively means how many connections can be open at the same time. If there is an requirement that says, it shall be 100 - let it be.

The workload of the queue is never an issue, as - if you carefully read lxcid comment - the heavy lifting of the connection delegates is performed on the dedicated thread. The queue just executes the start message of the operations and that's it.

The only issue that can possibly arise, is that if the number is large (say >=100) AND if the connections are spawned more or less at the same time AND if the system is dumb enough to spawn as many threads as required to start them simultaneously, then the system may run out of resources.

I don't think this is a real issue, though. Nonetheless, for this rare case, one could prevent to start those operations simultaneously through using a single semaphore (dispatch lib). This semaphore has initial value 1. When an operation is about to be started (from any thready) it must wait for the semaphore, until it becomes available. At the end of start method, the operation signals the semaphore (possibly scheduling on the connection thread to be 100% safe). That way, the -start methods are effectively serialized on will likely only use one thread, even though the queue has many connections.

The issue I have run into is that a high number of simultaneous connections causes all the operations to fail due to networking constraints, which are not considered by NSOperationQueue. Given this is a networking library, setting that value too high to successfully perform networking seems non-optimal.

If that's not a problem anymore (it's been a while since I checked) there's no reason to set it lower.

You may read RFC 2616, §8.1.4 "Practical Considerations":
"Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy".

If you connect to different servers, there is no such recommended limit. However, it should be obvious that each connection which is active will consume system resources. Some of these may have a hard limit set in the kernel, regardless of the amount of RAM installed on the machine/device.

The thing is, the library cannot suggest or recommend what you can or should do in this case. There are too many factors which are not under control by the library. You may find by trial and error what works best for your use case.

In any case, if you have an use case with 100 simultaneous connections - I would call that "advanced use case", you should really have a sound understanding of the underlaying mechanics (NSURLConnections) and profile and test your code thoroughly if you want to ship a highly optimized and robust application.

Otherwise, just go safe and limit the max simultaneous connections to, say 2 or 4.

I need to apologize (again) for providing some comments that is a bit out of the topic and misleading the conversation a bit. I going to avoid commenting on my iPhone and sending comments before I sleep.

Anyway, I run the example project and did face the issue mentioned.

I further investigate a bit by doing normal log debugging. I hook messages when starting the operation, starting the connection and receiving response in AFURLConnectionOperation.

What I realize while debugging on simulator (iOS 6) and a device (iPhone 5, iOS 6) is that the default settings have literally no limits. All its connections are opened and stalled. The best explanation is like what other have said, the network resources are exhausted. I believe it stall at opening too much network connection.

The funny thing is that, I ran the same test on simulator (iOS 5) and with the default settings, the stalling went away. I could no longer stall the network. It seems that in NSOperationQueueDefaultMaxConcurrentOperationCount behavior defers between iOS 5 and iOS 6. In iOS 5, there seems to be some restrictions while in iOS 6 its limitless.

I gave the problem further thoughts and list my points:

  • The problem is an edge case.
  • I do agree that using NSOperationQueueDefaultMaxConcurrentOperationCount is making AFNetworking a bit edgy because of its irregularities.
  • But NSOperationQueueDefaultMaxConcurrentOperationCount is known to be the "Apple decides the best way" approach and since we doesn't know what consideration Apple takes, dismissing it seems a bit harsh.
  • If anyone going WWDC, could anyone ask the Apple engineer about this question?
  • The 2 simultaneous connections is mainly a HTTP 1.1 recommendation, which is drafted in 1999. Its a per server/domain limitation which although seems perfect fit for AFNetworking (Each client which usually talk to a different domain can be limited individually), in modern browser like Firefox, this value have increase to 15 and in Safari is around 5. Browser need this setting to be good citizen though in a generic sense so this limit I believe doesn't apply as much to us.
  • In general, I prefers "Apple decides the best way" or "no limitation" approach. Mainly because setting a arbitrary number, people will keep asking why that number and I foresee that there will be no real good answer for that.
  • If a number really have to be set. I would vote against 2 because its based on the past.

Apple's already answered this in one of the previous WWDC talks. NSOperationQueueDefaultMaxConcurrentOperationCount is not the right value for network- or disk-bound queues; it's strictly for CPU-bound.

I missed your previous comment. I'm feel dumb.

Since NSOperationQueueDefaultMaxConcurrentOperationCount is only meant for CPU-bounded task, I would suggest the following:

- (id)init
{
    // any thread, but serialised by +sharedManager
    self = [super init];
    if (self != nil) {

        // Create the network management queue.  We will run an unbounded number of these operations 
        // in parallel because each one consumes minimal resources.

        self->_queueForNetworkManagement = [[NSOperationQueue alloc] init];
        assert(self->_queueForNetworkManagement != nil);

        [self->_queueForNetworkManagement setMaxConcurrentOperationCount:NSIntegerMax];
        assert(self->_queueForNetworkManagement != nil);

        // Create the network transfer queue.  We will run up to 4 simultaneous network requests.

        self->_queueForNetworkTransfers = [[NSOperationQueue alloc] init];
        assert(self->_queueForNetworkTransfers != nil);

        [self->_queueForNetworkTransfers setMaxConcurrentOperationCount:4];
        assert(self->_queueForNetworkTransfers != nil);
        .
        .
        .
    }
    return self;
}

The following code snippet is taken from MVCNetworking Sample Code. In there, 2 queue are created for 2 scenarios, generic network operation and long network transfer operation. I believe most people uses AFNetworking mostly for generic network operation, so I think NSIntegerMax is the best next option.

True that it doesn't prevent stalling but if anyone face with stalling issue, its more of a design issue and not really an AFNetworking issue. Also, the solution can be easily be setting it to a lesser number that make sense for their edge cases.

This avoids people question why do AFNetwork set an arbitrary number and allows a more consistent behavior between iOS.

That network connections get stalled on the client side may have many reasons and non of these is likely to be within in the realm of AFNetworking.

When you like to figure out where that might be coming from, you need to start your favorite network protocol sniffer, tcpdump, wireshark or any of the mitm tools and watch and analyze the HTTP protocol over the wire.

You will likely recognize that this is how the Internet works. Just consider this:

You may setup your own test HTTP server, which is single threaded and only processes one request at a time. The server runs on a certain OS whose kernel's TCP listen backlog is configured to accept at max 5 (the usual on BSD derived systems) new connections.

Since the server is single threaded, it has no knowledge that there are possibly more pending requests. Now, if you flood the server with requests, you might imagine what happens. Once the backlog queue is full (which is a parameter of the kernel), new HTTP connection requests will be rejected by the system right on the TCP level.

Your client may see a total different behavior when this same HTPP server architecture is installed on a big site, which runs 100 of those servers in parallel. In front of these, there is some kind of load balancer and a router which distributes the HTTP requests to the servers. The router may even queue incoming requests when all servers are busy, or not. The sys admin may also have decided to set the backlog queue size of each virtual machine the server will run to 1, in order to better control their distribution algorithm and optimize performance.

Now, imagine what your client is now seeing when it floods the server with requests.

But this is not even close to the real world. There may be proxies anywhere in between the client and server, single threaded asynchronous HTTP servers which process up to N requests simultaneously, or there may be multi process HTTP servers (unicorn for example) and there may be multi threaded HTTP servers, and all run behind some routers, load balancers, etc.

So, what your client is seeing when you flood a certain server depends heavily on the environment.

And, last but not least, there is NSURLConnection - probably the most obscure class in Cocoa. It tries to present a simple API for a quite complex problem. Whether NSURLConnection will manage its own queues in order to full fill the recommendation stated in RCF 2616, that is it queues a request to a certain server unless there are less then 2 active connections - I don't know.

Start your network protocol sniffer tool and share your experience! ;)

I'm not sure I like setting maxConcurrentOperationCount to NSIntegerMax. That's a lot of concurrent requests, especially on cell connection. I would rather default to something much lower, and if you need to be spamming and sending all of your requests as fast as possible, then it's on you to change that value.

This should be easy to prove once we have the test suite. NSIntegerMax would be worse than what's there now, though; this would mean there'd be no attempt at all to scale back.

A reasonable default value would rather be 1.

This means, that at any time and for this queue only one connection can be active. This is optimal for use cases, where the developer targets one certain server, and the server quickly handles the requests. Note that there can be possibly hundreds of pending requests in the queue - but only one is actually started!

This is even optimal in cases where the user queues a large number of requests, and then let the thing run. Since in most cases the bandwidth will be the limiting factor, one simultaneous connection at a time is still the optimal case then.

For any other value for maxConcurrentOperationCount, the developer needs to understand the consequences. In rare cases, a value up to 20 on devices and a value up to 100 on desktop machines may become optimal. In this case, the developer would have to ensure that no more than 2 simultaneous connections to one particular server have been established. And, it seems also only appropriate if the bandwidth cannot be saturated by these number of connections by the servers.

Any higher value is unlikely to be better. Rather, since each active connection will cost system resources, there may be a point where this becomes an issue - faster on devices with low memory.

I put a simple console test program where you can play with in order to see what happens. (much better than to explain it with thousands words)
https://gist.github.com/couchdeveloper/5556360

Note: method -doWork simulates the delegate's work. It executes on a serial queue, rather on a dedicated thread.

I don't think it worst. I don't believe I can agree on any number because they are picked without basis and these are per client/queue limit. Even if the limit is 8, if I have 5 clients or more, I can still end up stalling or exhausting the network resources. It's all dependent on my behavior.

Limiting it to 1 will just handicap AFNetworking so as to avoid edge cases.

Only people who tried to queue a large number of connections will face this issue and it probably signify to the user that he should really manage his queue. If we take away this edge case, it is like removing the ability to feels so that we don't feels pain, but we won't know it's harmful anymore.

In summary, I'm really against setting a number because:

  • we don't know the upper limit and it probably varies from device to device.
  • it's per client/queue limit and not a global limit.
  • it's hard to justify.
  • it hide what i foresee as an issue, creating too much request in a short period of time.
  • limit should not be too low a number because it a waste the dedicated network thread resources. We are mobile, network latency is not consistent.
  • like I said, the 2 simultaneous connection limit is just a good citizen recommendation. I had done extensive research in this argument before and assure you modern browser had increase the limit to 5-15 by default.
  • We are building an API client, not a web browser here.

I know my argument can be a little weak but if you think about what are we defending here and I just find that to constraints the system to protect against abuse is just not worth the trouble.
—
Sent from Mailbox for iPhone

On Sat, May 11, 2013 at 1:31 AM, Steven Fisher [email protected]
wrote:

This should be easy to prove once we have the test suite. NSIntegerMax would be worse than what's there now, though; this would mean there'd be no attempt at all to scale back.

Reply to this email directly or view it on GitHub:
https://github.com/AFNetworking/AFNetworking/issues/953#issuecomment-17733545

@lxcid We shouldn't put a bullet in a gun and hand it off to the developer, which is what we would be doing if we set it to NSIntegerMax. We should have a controlled default, and if the developer needs more juice allow him to set it to something higher.

I've seen arguments where people have wanted to use Reachability to determine what that number should be, and make it lower on Cell and higher on Wifi. Although I'm against adding additional default properties for that in the AF core, it wouldn't be hard to do with the Reachability callback block.

@lxcid: Your argument appears to me that was should sacrifice reliability for idealism. I strongly disagree, and I think my users would too. Values as low as 4 cause uploads to die over 3G.

I think @couchdeveloper is right that 1 is a good default.

  1. It will cause the fewest problems.
  2. It's easily changed by the developer.
  3. It's a common sense default.

I would agree that 1 is much more sensible than NSIntegerMax, but I would be ok with it being anywhere between 1-5 by default.

The foolproof value and the common case value is 1.

A way to make it clearer what this number means to the end user would help too. For example allowing the setting of a new ConcurrencyLevel property to NORMAL, AGGRESSIVE, or INSANE would be easier than asking the user to come up with their own precise values for maxConcurrentOperationCount.

Since everyone is against the value of NSIntegerMax, I would suggest what @couchdeveloper previously mentioned, a value of 2, because it conforms to http 1.1 standard of 2 simulatneuous connections per server, which each client usually only talks to one server endpoint so we kinda conforms to the idea easily.

Although in recent years browser have increased that value.
http://stackoverflow.com/questions/5751515/official-references-for-default-values-of-concurrent-http-1-1-connections-per-se

Wow -- another extremely lively thread. For whatever its worth, I would throw my vote behind @lxcid's proposal to default to 2 connections. 1 seems too low and anecdotal evidence indicates that 4/5 has issues over 3G

A value higher than 1 might already become suboptimal in environments where the bandwidth is low, unreliable and where the sender's device has restricted system memory. (You need to understand the TCP protocol why this is the case).

The possible effect when it becomes suboptimal is, that for every connection and for every packet that has been send but where the ACK didn't arrive in due time (a factor of the RTT), the underlaying TCP stack may resend the packet. This is true for the client and the server.

I fear, that in cases where the connection is not reliable and slow, any additional connection may make the end result worse. But this is rather suspicious - we can test and confirm this only in a real environment, or a special simulator.

So, a value of 1 on iOS, and 4 on Mac OS X! ;)

I stand by my point of no to arbitrary number that have no references.

This is what I want to avoid with NSIntegerMax in the first place, there's too many unknown to come up with a good number.

We doesn't know how the underlying NSURLConnection works, unless an Apple's Engineer can confirm anyone's finding, or have a test suites to show consistent behavior that support his point, it will be just theory and guessing work. I do not support modifying AFNetworking based on theory or guessing work.

My next preference is 2 because it is stated in HTTP 1.1 documentation and it is low enough (I consider it too low in today context as this is a standard is defined in 1999, 14 years ago).

AFNetworking throws a dedicated run loop thread, if you set it to 1, its a big waste of that resources, we might as well throw it in the main run loop.

Latency is not just outbound, it also inbound. Its not just affected by the pipe, it can be the languages it uses, the framework it uses, the logic of the tasks, etc. We can be waiting for a slow server while we are on an LTE network, which is faster than a lot of broadband connections. The server might setup load balancer and a single server endpoint is served by multiple server.

The future will be more concurrent, the value is 1 is too backward to me. the value of 2 is already quite backward IMO, but it seems to be a good compromise and have a good reference if someone ever questioned the decision.

If you think 1 is a good number, just bring up your mobile safari, run a concurrent test on it and you find out a similar result as below:

2013-05-11 16 57 30

Tested with http://mtest.cloud4sites.com/mobile/

5 concurrent connections per server, 20 maximum concurrent connections for iPhone 5 running iOS 6 on LTE. This number may or may not varies between devices and/or connections.

This number changes between browser.

Setting it to 1 is just too degrading to AFNetworking IMO, so no for me to 1, considering most users might never modify this

One more point I forgot to mentioned.

NSURLConnection can be pipelined. So if you decides to open 1 connection only, you are preventing Apple to use HTTP Pipelining.

I agree with you in many points, lxcid. However, _default_ settings should be _defensive_. Which means, that for any uncaring use the behavior of AFNetworking shall be as robust as possible.

Furthermore, it might be true that many of the recommendations and regulations are decades old, but the HTTP 1.1 standard still applies. HTTP 2.0 will bring significant improvements to this special case, since then we get an asynchronous behavior.

In your situation though, since you have enough experience with this stuff, you should just be able to tailor the behavior of AFNetworking as you desire and as you seem is appropriate for your particular app. I could imagine that in your app you check the availability of a LTE network and set the max concurrent connections to say, 4.

sidenote:
is there a way to detect LTE on IOS? i think i have read that currently there is no way to detect the presence of LTE

Apple's recommendations are in WWDC sessions.

Let's start over here.

What is the goal of AFHTTPClient? The goal of AFHTTPClient is to connect to a web-based API or to download a batch of pages.

The first case is, I think, the one that's most important because it's most difficult. NSURLRequest is not Safari, and AFHTTPClient is not a web browser. An AFHTTPClient connected to an API is just as likely to be posting data as downloading it, and uploading is usually much more restrictive.

Setting this to NSOperationQueueDefaultMaxConcurrentOperationCount or NSIntegerMax is intentionally breaking everyone. Setting it to 2 is arbitrary. You seem to think it says only "We think 1 is too low," but it also says "We think they're writing a web browser or an application that can do these requests in parallel."

The only value that makes sense for posting to an API is 1. AFHTTPClient is for connecting to an API; the default should be serially. The developer can increase it if they're sure it's safe to with their API.

If you set it to 1, a small number of people are going to ask "Why does only one connection happen at a time?" The answer is "Oh, that's the default because it's safe. If you set it to more than 1 you're probably going to get failed uploads or operations completing out of order. Are you sure that's okay with your service? Go ahead and change it."

If you set it to something else, more people are going to ask "Why are all my connections timing out or not completing in order?" Though they're not going to realize it's just completions going out of order; they're going to think it's just out of They're going to get operation 1 and 2 started, then 2 is going to complete and 3 will start… 3 will finish and 4 will start… and 1 still hasn't completed!

Your answer: "Oh, we decided to make it do 2 operations at once. That's not causing you major problems, is it? You can set it to 1." The natural next question is "WHY WOULD YOU DO THIS?!? to which you can only answer "Well, we thought 1 _sounded too low_."

That's not a good enough answer, and this is not just an edge case.

Maybe there should be a subclass here for AFHTTPDownloadClient?

LTE vs. 3G is a nonstarter, too; this is also described in the WWDC sessions. You cannot rely on the connection type to make decisions about its quality.

Finally: Nowhere does Apple say that limiting to one connection will disable connection pipelining. It would be nonsensical for them to do so; pipelining is just holding the connection open.

To me, I'm alright with large number or small number. The reason I didn't like 1 is because AFNetworking roll out a dedicated thread and explicitly schedule run loop over there. Create operation queue and create operation to wait for connection delegate just so we can run network request serially?

It's understandable AFNetworking has issue with NSIntegerMax. It will be alarming if AFNetworking have difficulties running a reasonable amount of network asynchronously.

This is what I want to avoid all along, a discussion to agrue how many concurrent number we gonna set. Because there's so many counter arguments.

You have your points. I am standard obsess so I will always looks for reference point. Http 1.1 standard or running test on mobile safari or looking at how Quinn the Eskimo wrote. It's just my preference but mean little as ultimate it's mattt choice.

If at the end mattt decides to set 1. I'm not gonna argue.

As for pipelining if I'm not wrong it meant sending next request without waiting for the previous to response. While your description seems to be keep alive behavior.

As for ability to detect LTE and 3G, that I have not seen in the public though. I hope apple open that up in iOS 7. Will be useful.

As you see, we can be discussing about arbitrary number for the next 10 days and it will mean very little for the project. I shared my preference and thought process. If people aren't receptive of it, I'm okay with it. It ultimately boil down to what kind of expectation to set for AFNetworking users. :)

Whoops! You're right that I was thinking of keep alive. Thanks.

As the old saying goes:

There are 3 numbers in programming: 0, 1, & ∞

Right now, the limit is effectively set to ∞, since the behavior of NSOperationQueue to dynamically resize the limit based on memory conditions does not work for networking, which is not bound by the same constraints.

NSURLConnection's implementation details are hidden to us, so RFC guidelines are going to be mostly irrelevant. In an ideal world, it would be able to accommodate this better, but all we can do is file Radars and cross our fingers each June.

0 is out the question, for obvious reasons.

So that apparently leaves us with 1 as the only viable answer. Great in theory, but in practice, the performance is _atrocious_. Setting it to 1 means actually waiting for things to load over 4G, compared to the great performance with NSOperationQueueDefaultMaxConcurrentOperationCount. 2 helps, and 3 is even better, and so on. Now we're back to infinity.

This is the first issue I've seen about NSOperationQueueDefaultMaxConcurrentOperationCount causing a problem. @kennycarruthers noted that it was a bit of an extreme case and would be willing to set this property to a reasonable value to fix it (even better if this was documented).

All things considered, the failure case of requests stalling (sometimes to the point of timing out) is not all that bad—it's noticeable, but not fatal. Pegging performance to a snail's pace with only 1 concurrent operation is arguably worse.

...which I guess is what I'm arguing: ∞ > 1, and although there may be some N such that N > ∞, it's an arbitrary value, which is likely dynamic according to device, network conditions, phase of the moon, etc.

For now, NSOperationQueueDefaultMaxConcurrentOperationCount stands. If anyone wants to shake down Quinn, et al. at WWDC for the correct answer, please be my guest. We'll reconvene next month.

MKNetworkKit has an interesting take on this. maxConcurrentOperationsCount is changed dynamically, using current reachability status.

The defaults are 6 for WiFi, 2 for cellular. I'm not saying that is a correct approach, but has this been considered?

https://github.com/MugunthKumar/MKNetworkKit/blob/master/MKNetworkKit/MKNetworkEngine.m#L206

I don't think that is the right approach. WiFi doesn't mean good WiFi; WiFi can easily be back by cellular, or a poor connection shared with way too many people.

The right way to handle this is to scale up or down as you understand the capabilities of the network. Unfortunately, that requires the the operations be automatically retried in the case where the error looks like a saturated network — which they're not.

@DenHeadless Agree with @tewha. In actuality, this is a really _bad_ idea. "WiFi always > WWAN" is one of the most pervasive myths about mobile networking.

And to be clear, @mattt, I'm not saying AFNetworking _should_ retry when the network's saturated. At least not by default. I think that's something that should be built separately, tested, and simply available. (And I'm not saying YOU should build it, either!)

@DenHeadless, I personally know Mugunth and had chat with him about his rationale behind that in the past. It does have its merit. At the end of the day, I can't agree with him fully, but respect his choice nevertheless.

Much of my argument here is a continuation of my discussion with him and thus all consideration are weighted in.

@tewha argument is all it need to take to debunk the myth. In today networking world, the assumption of WiFi source is a broadband isn't going to hold that often as before. Similarly, LTE has a faster throughput than ADSL in some cases.

So, as the world evolves, we shouldn't be pinning ourselves to the past.

Was this page helpful?
0 / 5 - 0 ratings