1

We're in the process of refactoring all, or a large portion of our .NET 4.6 MVC WebAPI controller methods to async methods.

This seems like it will work well for methods that have a lower level invocation of a method that is awaitable such as SQL command execution; however we are utilizing an in-memory distributed caching framework by Alachisoft called NCache (4.6 SP2 to be exact), which does not offer any truly asynchronous methods.

Would it be worth creating an async helper method that would expose a awaitable Task<object> return type?

Traditionally using the NCache API, you Get an object from cache by a Key in the following usage;

NCacheObject.Get(string);

The proposal is to create a helper method of the following;

protected static async Task<Object> GetAsync(string key, Cache nCache)
{
    Task<Object> getTask = new Task<Object>(() => nCache.Get(key));
    getTask.Start();
    return await getTask.ConfigureAwait(false);
}

So that it would then allow full waterfall of async methods up to the entry controller method as such;

public static async Task<Tuple<List<SomeModel>, bool, string>> GetSomeModelList(string apiKey)
{
    return newTuple<List<SomeModel>, bool, string>(await GetAsync("GetSomeModelKey", CacheInstance).ConfigureAwait(false), true, "Success message");
}

And finally the controller method;

[HttpGet, Route("Route/Method")]
public async Task<ResponseOutputModel<List<SomeModel>>> GetSomeModelList()
{
    ResponseOutputModel<List<SomeModel>> resp = new ResponseOutputModel<List<SomeModel>>();

    try
    {
        Tuple<List<SomeModel>, Boolean, String> asyncResp = await CacheProcessing.GetSomeModelList(GetApiKey()).ConfigureAwait(false);

        resp.Response = asyncResp.Item1;
        resp.Success = asyncResp.Item2;
        resp.Message = asyncResp.Item3;
    }
    catch (Exception ex)
    {
        LoggingHelper.Write(ex);
        resp.StatusCode = Enumerations.ApiResponseStatusCodes.ProcessingError;
        resp.Success = false;
        resp.Message = ex.Message;
    }

    return resp;
}

This would complicate the refactoring, as the original methods actually had output parameters for bool success and string message; but it seems this can be accomplished in a decent and quick manner using a Tuple<>; otherwise we could just create a return type model.

To do this properly, there would be many hundreds of methods to refactor; quite an undertaking.

  1. Would this work as expected, and be the best solution to accomplish the objective?
  2. Is it likely worth all of the effort required, with the end goal of increasing scalability and subsequently "performance" of the web servers?
Sivart
  • 315
  • 1
  • 3
  • 12

2 Answers2

4

Would it be worth creating an async helper method that would expose a awaitable Task return type?

Nope.

The proposal is to create a helper method of the following

This just queues in-memory work to the thread pool.

(Side note: The task constructor should never be used. Ever. If you need to queue work to the thread pool, use Task.Run instead of the task constructor with Start)

Would this work as expected, and be the best solution to accomplish the objective?

Is it likely worth all of the effort required, with the end goal of increasing scalability and subsequently "performance" of the web servers?

These are the same question. The objective is scalability; asynchrony is just one means to help you accomplish it.

Asynchrony assists scalability on ASP.NET because it frees up a thread that can process other requests. However, if you make methods that are asynchronous by using another thread, then that doesn't assist you at all. I call this "fake asynchrony" - these kinds of methods look asynchronous but they're really just synchronously running on the thread pool.

In contrast to true asynchrony, fake asynchrony will actually hurt your scalability.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Interesting. I was hoping you would see this and comment; I was reading your blog posts earlier. So the issue I've seen thus far is that the `Get` method for NCache is incurring a substantial wait time as it awaits latency of network traffic, and the processing of the request from the server itself. After further review, it seems the only real items I could async/await are going to be DB calls for ExecuteNonQueryAsync; the methods that fill and return data tables don't seem to be adequately awaitable either. – Sivart Sep 15 '16 at 23:52
  • @Sivart: No, the (very old) data table APIs wouldn't work well with async, even if MS did try to write them. The newer EF ones are much more async-friendly. Your best bet re NCache is to raise an issue with their developers. – Stephen Cleary Sep 16 '16 at 00:07
1

As this is a memory cache and Im saying this out of any direct experience with NCache but experiences with other cache systems.

  1. Yes, this would work of course. I'd rather opt for a response struct or a generic class that allows me to define my response type (if it's possible at all). Tuples are not bad, probably would be more performant if you opt for classes instead. But they are not easy on the eyes.
  2. Now, when it comes to performance, there is one question though. Here's a cache server for you and you want fast read-write of course. Accessing a memcache should be easy. As you're directly accessing the memory here, from a performance point of view, it won't really give you much. Are you making sure your code is fully async and you're using the threadpool properly to make things alright? Yes, of course but all it would ever do is add an extra layer or work when you're accessing your cache.
  3. And the moment you're going for async, please do make sure that your memory cache is thread safe. :)
Swagata Prateek
  • 1,076
  • 7
  • 15