6

I am working with a pre-existing C# ASP.NET MVC webapplication and I'm adding some functionality to it that I can't decide whether or not to make async.

Right now, the application home page just processes a page and a user logs in. Nothing more, and nothing asynchronous going on at all.

I am adding functionality that will, when the homepage is visited, generate a call to a Web API that subsequently calls a database that grabs an identifier and returns it to an HTML tag on the home page. This identifier will not be visible on the screen, only on the source/HTML view (this is being added for various internal tracking purposes).

The Web API/database call is simple, just grab an identifier and return it to the controller. Regardless, I'm wondering whether the app should make this call asynchronously? The website traffic isn't immense, but I'm still wondering about concurrency, performance and future scalability.

The one catch is that I'd have to make the entire ActionMethod async and I'm not sure what the affects of that would be. The basic pattern, currently synchronous, is below:

    public ActionResult Index()
    {
        var result = GetID();

        ViewBag.result = result.Data;

        return View();
    }

    public JsonResult GetID()
    {
        var result = "";
        var url = "http://APIURL/GetID";

        using (WebClient client = new WebClient())
        {
            result = client.DownloadString(url);
        }

        return Json(result, JsonRequestBehavior.AllowGet);

    }

Any thoughts?

David Pine
  • 23,787
  • 10
  • 79
  • 107
ewomack
  • 607
  • 9
  • 23
  • 1
    If you can use the async/await keywords, always use async. If you have to use the legacy patterns, don't use it unless you got a current scaling problem-- it drives up the complexity level way beyond what it earns. – MatthewMartin Jun 13 '16 at 17:16

3 Answers3

4

First and foremost, realize the purpose of async, in the context of a web application. A web server has what's called a thread pool. Generally speaking, 1 thread == 1 request, so if you have a 1000 threads in the pool (typical), your website can roughly serve 1000 simultaneous requests. Also keep in mind that, it often takes many requests to render a single resource. The HTML document itself is one request, but each image, JS file, CSS file, etc. is also a request. Then, there's any AJAX requests the page may issue. In other words, it's not uncommon for a request for a single resource to generate 20+ requests to the web server.

Given that, when your server hits its max requests (all threads are being utilized), any further requests are queued and processed in order as threads are made available. What async does is buy you some additional head room. If there's threads that are in a wait-state (waiting for the results of a database query, the response from a web service, a file to be read from the filesystem, etc.), then async allows these threads to be returned to the pool, where they are then able to field some of those waiting requests. When whatever the thread was waiting on completes, a new thread is requested to finish servicing the request.

What is important to note here is that a new thread is requested to finish servicing the request. Once the thread has been released to the pool, you have to wait for a thread again, just like a brand new request. This means running async can sometimes take longer than running sync, depending on the availability of threads in the pool. Also, async caries with it a non-insignificant amount of overhead that also adds to the overall load time.

Async != faster. It can many times be slower, but it allows your web server to more efficiently utilize resources, which could mean the difference between falling down and gracefully bearing load. Because of this, there's no one universal answer to a question like "Should I just make everything async?" Async is a trade-off between raw performance and efficiency. In some situations it may not make sense to use async at all, while in others you might want to use it for everything that's applicable. What you need to do is first identity the stress points of your application. For example, if your database instance resides on the same server as your web server (not a good idea, BTW), using async on your database queries would be fairly pointless. The chief culprit of waiting is network latency, whereas filesystem access is typically relatively quick. On the other hand, if your database server is in a remote datacenter and has to not only travel the pipes between there and your web server but also do things like traverse firewalls, well, then your network latency is much more significant, and async is probably a very good idea.

Long and short, you need to evaluate your setup, your environment and the needs of your application. Then, and only then, can you make smart decisions about this. That said, even given the overhead of async, if there's network latency involved at all, it's a pretty safe bet async should be used. It's perfectly acceptable to err on the site of caution and just use async everywhere it's applicable, and many do just that. If you're looking to optimize for performance though (perhaps you're starting the next Facebook?), then you'd want to be much more judicious.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
3

Here, the reason to use async IO is to not have many threads running at the same time. Threads consume OS resources and memory. The thread pool also cal be a little slow to adjust to sudden load. If your thread count in a web app is below 100 and load is not extremely spikey you have nothing to worry about.

Generally, the slower a web service and the more often it is called the more beneficial async IO can be. You will need on average (latency * frequency) threads running. So 100ms call time and 10 calls per second is about 1 thread on average.

Run the numbers and see if you need to change anything or not.

usr
  • 168,620
  • 35
  • 240
  • 369
1

Any thoughts?

Yes, lot's of thoughts...but that alone doesn't count as an answer. ;)


There is no real good answer here since there isn't much context provided. But let's address what we know.
  1. Since we are a web application, each request/response cycle has a direct impact on performance and can be a bottleneck.
  2. Since we are internally invoking another API call from ours, we shouldn't assume that it is hosted on the same server - as such this should be treated just like all I/O bound operations.

With the two known factors above, we should make our calls async. Consider the following:

public async Task<ActionResult> Index()
{
    var result = await GetIdAsync();

    ViewBag.result = result.Data;

    return View();
}

public async Task<JsonResult> GetIdAsync()
{
    var result = "";
    var url = "http://APIURL/GetID";

    using (WebClient client = new WebClient())
    {
        // Note the API change here?
        result = await client.DownloadStringAsync(url);
    }

    return Json(result, JsonRequestBehavior.AllowGet);
}

Now, we are correctly using async and await keywords with our Task<T> returning operations. This will help to ensure ideal performance. Notice the API change on the client.DownloadStringAsync too, this is very important.

David Pine
  • 23,787
  • 10
  • 79
  • 107