Wednesday 14 January 2015

Part 2: Async requests in ASP.NET MVC 4 when working with Session [Thread Starvation my A$$]

This is a multi-part post dedicated to Async requests in ASP.NET MVC 4 when working with Session. Be sure to follow this series starting from the first post to gain a clear understanding.


Catch up from the last post

So in our last post it looked like it was Thread Starvation that was causing the all synchronous request to be queued and run sequentially. NOT! 

It wasn't Thread Starvation after all

So for thread starvation to occur, I should have exhaused all of the 5000 threads that were available. Seeing that I was hosting my application locally & was the only person accessing my website I couldn't have exhaused all my available threads (specified by MaxWorkerThreads). So we could deduce that it wasn't Thread Starvation.

What is it then?

Looks like i overlooked a key piece of information in my last post. The requests that were queuing up were all a part of the same session

How thread pools really work

On the Web server, the .NET Framework maintains a pool of threads that are used to service ASP.NET requests. When a request arrives, a thread from the pool is dispatched to process that request. If the request is processed synchronously, the thread that processes the request is blocked while the request is being processed, and that thread cannot service another request.

Access to ASP.NET session state is exclusive per session, which means that if two different users make concurrent requests, access to each separate session is granted concurrently. However, if two concurrent requests are made for the same session (by using the same SessionID value), the first request gets exclusive access to the session information. The second request executes only after the first request is finished. [Info Src Link]

What Microsoft recommends

The second session can also get access if the exclusive lock on the information is freed because the first request exceeds the lock time-out. If the EnableSessionState value in the @ Page directive is set to ReadOnly (or annotate the controller with [SessionState(SessionStateBehavior.ReadOnly)]), a request for the read-only session information does not result in an exclusive lock on the session data. However, read-only requests for session data might still have to wait for a lock set by a read-write request for session data to clear.

My solution

My original request method needed to get converted to an async operation achieved easily with async-await. In-spite of using async-await, the requests still ran sequentially due to the thread lock imposed by ASP.NET session state. Seeing that I needed access to my session state & possibly needed to save data to it, I couldn't simply disable the session state behavior. To find out what I ended up doing click here (Coming soon) (I'll include some code samples) ...

Sync v/s Async

Microsoft recommends the following ..
In general, use synchronous pipelines when the following conditions are true:
  • The operations are simple or short-running.
  • Simplicity is more important than efficiency.
  • The operations are primarily CPU operations instead of operations that involve extensive disk or network overhead. Using asynchronous action methods on CPU-bound operations provides no benefits and results in more overhead.
In general, use asynchronous pipelines when the following conditions are true:
  • The operations are network-bound or I/O-bound instead of CPU-bound.
  • Testing shows that the blocking operations are a bottleneck in site performance and that IIS can service more requests by using asynchronous action methods for these blocking calls.
  • Parallelism is more important than simplicity of code.
  • You want to provide a mechanism that lets users cancel a long-running request.