New NSURLSession for every DataTask overkill?

Just saw some code where in a method:


- a NSURLSession is created from a configuration as an automatic

- a dataTask is created by the session, and "resumed"

- method returns


Yikes! In my code I've always created a single session, saved it as an ivar, then gotten and "resumed" dataTasks, only releasing the session when the object is released.


Comments?

Accepted Reply

Yikes!

Yikes indeed. This is a common anti-pattern, one that we specifically warned against at at WWDC this year. Creating a session per request is inefficient both on the CPU and, more importantly, on the network. Specifically, it prevents connection reuse, which can radically slow down back-to-back requests. This is especially bad for HTTP/2.

We encourage folks to group all similar tasks in a single session, using multiple sessions only if you have different sets of tasks with different requirements (like interactive tasks versus background download tasks). That means that many simple apps can get away with using a single statically-allocated session.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Yikes!

Yikes indeed. This is a common anti-pattern, one that we specifically warned against at at WWDC this year. Creating a session per request is inefficient both on the CPU and, more importantly, on the network. Specifically, it prevents connection reuse, which can radically slow down back-to-back requests. This is especially bad for HTTP/2.

We encourage folks to group all similar tasks in a single session, using multiple sessions only if you have different sets of tasks with different requirements (like interactive tasks versus background download tasks). That means that many simple apps can get away with using a single statically-allocated session.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Recently I met a strange thing when folks to group all similar tasks in a single session. The situration are as follows:

1. I create an URLSession with the default configuration.

2. Use the session to create NSURLSessionTask to request remote image data.

3. continuously create new request and cancel the old request, quickly

4. After a few seconds, the TCP connection will not return the image data, all your request will return NSURLError -1001, but other tcp connection are all right. And this situration will last for a few minutes, then it works fine as former.


All my requests url are HTTPS, and SDWebImage version 4.1 has the same problem. How can I solve this problem?

First up, some factoids:

  • The NSURLSession ‘owns’ the TCP connections used by the tasks done within that session.

  • NSURLSession puts limits on how many TCP connection it will open. This is required because opening a bazillion TCP connections is very harmful (to both the server and the client).

  • When you start a task one of there things happens:

  • If there’s a suitable connection in place, the task uses that connection.

  • If there’s not suitable connection in place and there’s no too many connections open, the session starts a new connection for that task.

  • Otherwise, the session queues the task waiting for a connection to become available.

  • The definition of “suitable connection” is complex but the one big factor is the HTTP version. HTTP/2.0 can multiplex many requests over a single connection, making it much more likely that a suitable connection will be available.

It’s hard to say what’s going on with your specific test case but it’s likely that you’re running the session out of connections. If you really want to get to the bottom of it, you should take a look at a CFNetwork diagnostic log (see QA1887 CFNetwork Diagnostic Logging), which reveals the whole internal process.

However, understanding what’s going on does not necessarily help you avoid it. To that end, my question centres around this:

3. continuously create new request and cancel the old request, quickly

Why are you doing this?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks for your reply,

Regarding to you question: 'continuously create new request and cancel the old request, quickly' .

What we do here is to simulate a real-time scene. Say we have a table view which has a bunch of pictures, it will start to download a picture when user scroll to a particular cell. We are also trying to minimize the mobile data usage when user scrolls very fast so that we would like to cancel thoses downloading requests when the cell is being scrolled off the screen. Then is comes the "create new requests and cancel the old requests scene".

Are you issuing the requests as the cell comes on screen and then cancelling them when the cell goes off screen? If so, that’s not a great approach because it butts up against fundamental HTTP 1.1 limits. HTTP 1.1 has no way to cancel in-flight requests. The only cancellation option for the client is to drop the connection. And the fundamental problem with that, above and beyond the NSURLSession issue we’re discussing here, is that the bandwidth you save by cancelling the request tends to get lost in re-establishing the connection. Worse yet, re-establishing the connection results in higher latency for any subsequent requests.

Can you guarantee that the server you’re talking to is HTTP/2? If not, you’ll need to rethink your approach regardless of any NSURSession problems you’re seeing. Let me know if that’s the case and I’ll reply with my suggestions.

On the other hand, if you can guarantee that you’re talking to an HTTP/2 server then we should explore the NSURLSession side of this.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

No, this is not the issue I'm facing - but your response is of interest in any case!

Hi, recently I found that if I start a session and cancel it after a second, repeat the same request for about ten or more times, then when a new request is started, in http proxy I found this new request will always gets time out. But if I debug the request in somethings like postman, the request is finished ok. So the server seems ok, but the client, it's iOS, is not.


So I changed my solution, and every time when I want to get a new task, I create a new `URLSession`, the problem still exists, except now I should retry creating tasks and canceling for about 50 times. And sometimes the request isn't seeied in http proxy, it seems the URLSession doesn't start the tasks.


I can guarantee that I'am using HTTP 2.0


Some thoughts about this?

if I start a session and cancel it after a second, repeat the same request for about ten or more times

It sounds like this particular pattern is triggering some latent bug with

NSURLSession
. Given that you can reproduce it at will, that’d make a great bug report. Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi eskimo,


I found the same issue in my app. I have reported the bug in Bug Report, and the number is 48137725.


As you mentioned, it's bad to create a new session for every request, but the bug made us to create the new session.


So is there any suggestion?