17

I was looking at ASP.NET MVC 5 templates and I have notice that many of the actions and marked as async:

public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { }

When should I do this on an MVC action? When it contains access to a database?

If I call a repository inside the action should I also use Task to make it async?

Yuck
  • 49,664
  • 13
  • 105
  • 135
Miguel Moura
  • 36,732
  • 85
  • 259
  • 481

3 Answers3

11

The core of your questions is: When should I make my MVC actions async? See http://blogs.msdn.com/b/rickandy/archive/2009/11/14/should-my-database-calls-be-asynchronous.aspx for a good discussion of the issue. He talks about the database only, but his points carry over.

Essentially, almost never call the database in an async way.

For database applications using async operations to reduce the number of blocked threads on the web server is almost always a complete waste of time.

Don't be detracted by people telling you to always use async IO if possible. Async is all the rage right now. Lot's of irrational advice is being spread.

usr
  • 168,620
  • 35
  • 240
  • 369
  • 2
    Should be changed, the referenced article has an update from 28 November 2012 stating..."The combination of await, async, and the Task object makes it much easier for you to write asynchronous code in .NET 4.5. Now that EF 6 is supporting Async Query and Save, you should take advantage of asynchronous programming" – Paul Hatcher Apr 02 '15 at 16:02
  • 1
    @PaulHatcher I don't agree with that conclusion at all. See my other pieces on this topic: http://stackoverflow.com/a/25087273/122718 and http://stackoverflow.com/a/12796711/122718. If the entire community jumps on a new cool technology and nobody really says why then you should start asking questions. – usr Apr 02 '15 at 16:25
9

Entity Framework 6 (used by default with MVC 5) now supports async database calls, so the action method signatures have been update to reflect async being used. The simple answer is that whenever you have a task that could potentially involve waiting, use async. Hopefully, your database queries won't take long enough to roundtrip to actually benefit much from async, but if your database falls down or is being hammered particularly hard, it'll at least help to not deadlock IIS in the process.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • This advice needs justification. – usr Apr 02 '15 at 16:28
  • @usr: What kind of justification are you looking for? The justification is already there. You'll give IIS some breathing room to field additional requests if the database server is taking a particularly long time responding to requests for some reason. The nature of async means that while the thread is in a wait-state it can be returned to the pool to do other work instead of sitting there locked, waiting for the task to complete. – Chris Pratt Apr 06 '15 at 20:07
  • 1
    If you expect the DB to respond in 10ms on average and it takes 10s you can return all the threads you want. The app is down. Threads are not scarce in ASP.NET. There are hundreds by default and no common DB can benefit from so many parallel requests. The case for async DB calls is weak. – usr Apr 06 '15 at 20:19
  • 1) Threads are scarce. In a typical setup, a web server might have only 1000 to play with. 2) Each request requires at least one thread, so the fact that you have "hundreds" is really not that impressive. If you're fine with your web server grinding to a halt if it has to field more than 1000 simultaneous request, go ahead and stick with sync. 3) You're assuming that every single web request must make a call to the DB. Just because the DB is running slow or down completely doesn't mean that your website has to go down with it. – Chris Pratt Apr 06 '15 at 20:33
  • 1
    Almost no website in the world needs to handle 1000 concurrent requests. For example Stack Overflow never does despite being a top 100 site. This only happens if all those requests do is wait for something for a long time. Like a webservice or a websocket. Those are great cases for async. A database is totally overloaded if it gets 1k concurrent requests. This architecture is not viable so the point of sync or async is moot here. To phrase it differently: You don't get more throughput or better latency by making DB calls async. Which is the only point in doing so. – usr Apr 06 '15 at 20:49
  • @usr The situation is different with SPA application where a single user action may and often does result in more requests sent to the web server. If one user action results in 3 ajax requests then it needs only 333 concurrent users to have 1,000 HTTP requests in web server. It may not be many for most of public websites but enterprise appications what are used by many companies with many employess have extremely high peaks in the mornings and after lunch breaks. – user3285954 Apr 11 '16 at 16:55
  • @usr Of course these websites are load balanced so it's unlikely that 300 users will fill up the HTTP queue but is a real concern and is easy to fix so why rely only on horizontal scaling? It would be like not releasing memory because RAM is dirt cheap. – user3285954 Apr 11 '16 at 16:55
  • @user3285954 1000 concurrent users do not cause 1000 concurrent requests. Users have *huge* pause times such as 10 seconds. They cause 1000 requests in 10 seconds, that's 100 per second. Not even 100 concurrently, 100 in total in one second. At 100ms per request that's 10 concurrent requests at all times.; Async is not an easy fix at all, it costs labor, decreases code quality, infects the entire call chain and increases bug rate. – usr Apr 11 '16 at 19:34
  • @usr It all depends on application. Some applications generate one or more server events on every key press and mouse move, it's not always about people reading news. And I'm talking about peaks, especially in the morning when let's say thousands of employees of your customers log in to the system hosted by you and start work. Or real time applications like stock market and betting. Async is free for new development, and if it's a refactoring then obviously has a cost like any other fix what may or may not worth the benefits. It all depends on concrete facts, not so hard to decide. – user3285954 Apr 11 '16 at 23:02
  • @usr I wouldn't say that increases the bug rate more than any other change. TPL is very robust and if one does their homework and knows what ConfigureAwait(false) is for then they prevented 99% of issues. Another point is that more and more API becomes async only (i.e. HttpClient what is the core to access HTTP APIs) so basically you can either expose the resulting tasks to all layers (recommended) or you can hide the async API with sync methods (not so recommended). – user3285954 Apr 11 '16 at 23:08
  • @user3285954: Good point. All of the EF sync methods actually just proxy to the async method, anyways, so whether you use async or not, you still have the overhead. You might as well make use of task-based architecture. – Chris Pratt Apr 12 '16 at 12:37
5

Here's an article what lists some uses cases when using tasks may have benefit and some uses cases when may have the opposite effect. The answer is not this simple every time, this is why the last point about testing.

Quote from http://www.asp.net/mvc/overview/performance/using-asynchronous-methods-in-aspnet-mvc-4

In general, use synchronous methods for the following conditions:

  • 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 methods for the following conditions:

  • You're calling services that can be consumed through asynchronous methods, and you're using .NET 4.5 or higher.
  • The operations are network-bound or I/O-bound instead of CPU-bound.
  • Parallelism is more important than simplicity of code.
  • You want to provide a mechanism that lets users cancel a long-running request.
  • When the benefit of switching threads out weights the cost of the context switch. In general, you should make a method asynchronous if the synchronous method waits on the ASP.NET request thread while doing no work. By making the call asynchronous, the ASP.NET request thread is not stalled doing no work while it waits for the web service request to complete.
  • Testing shows that the blocking operations are a bottleneck in site performance and that IIS can service more requests by using asynchronous methods for these blocking calls.
user3285954
  • 4,499
  • 2
  • 27
  • 19