8

I have a WebApi that for each incoming request, calls 2 separate webservices, performs some post processing and returns the results.

The first webservice call is cached locally for 1 hour, and the data in this determines the query to the 2nd webservice. The 2nd webservice is called on each incoming request. After that 2nd request is made, each result is processed with business logic and returned back to the client response.

The call to the 2nd webservice cannot be asynchronous because it is using a 3rd party dll that does not allow the await keyword. What I've done, is wrapped the 2nd webservice call and post processing into one async function, which is called from the controller.

// /api/controller/news?key=a&state=b
public async Task<HttpResponseMessage> GetNews(string key, string state)
    {
         // call to first webservice if not in cache
         if (JsonConfig != null && JsonConfig.Configuration.NewsQuery.ContainsKey(key))
        { 
            var results = await SearchProxyProvider.Search(filters.All, filters.Any, filters.None, filters.Sort, 100, 0, true, state, true);
            int totalCount = results.TotalCount;

            return Request.CreateResponse(HttpStatusCode.OK, results);
        }
    }


// Helper class method
public async Task<ItemCollection<Item>> Search(List<FieldValuePair> allFilters, List<FieldValuePair> anyFilters, List<FieldValuePair> noneFilters, SortedFieldDictionary sortBy, int pageSize = 100, int pageNumber = 0, bool exact = true, string stateFilter = null, bool getAllResults = true)
        {
            // call to 2nd api
            search = SomeApi.Search(allFilters, anyFilters, noneFilters, pageSize, pageNumber, exact,
                                               sortBy, null, WebApiConstant.Settings.CustomFields, true);

            // post processing on search results
            return search;
        }

Because the call to the first webservice is cached locally, I don't really see a huge benefit to making this asynchronous.

I'm just looking to see if this approach is totally wrong, or right.

2
  • How many calls to web api do you expect per hour, on average? Commented Aug 13, 2013 at 14:55
  • Looking at 1000 concurrent users. There will be multiple servers load balancing the webapi, 2-3. An initial application download of 400K users is expected. Commented Aug 13, 2013 at 23:06

2 Answers 2

8

The first webservice call is cached locally for 1 hour, and the data in this determines the query to the 2nd webservice.

You can do some tricks with AsyncLazy<T> (e.g., from my blog) and cache that instead. That gives your requests a way to (asynchronously) wait for a refresh, and you won't hit Service1 multiple times when you need to refresh the data, regardless of the number of simultaneous requests.

The call to the 2nd webservice cannot be asynchronous because it is using a 3rd party dll that does not allow the await keyword.

That's a bummer. Do lean on them to fix it. :)

What I've done, is wrapped the 2nd webservice call and post processing into one async function, which is called from the controller.

There's no point to that. The compiler will warn you that your "async" method is in fact synchronous.

If it's synchronous, then just call it synchronously. On the server side, there's no point in wrapping it in Task.Run or anything like that.

I have some slides available from an "Async on the Server" talk I gave Monday at ThatConference, which you may find helpful. (They have animations of how asynchronous requests are handled and why "fake asynchronous" methods don't help on the server side).

Sign up to request clarification or add additional context in comments.

7 Comments

So if none of my web calls can be called asynchronously, but I have a method that performs a long running task, how can I make that asynchronous? (Server-side WebAPI)
@mickyjtwin: You shouldn't. You can wrap it in Task.Run, but that won't gain you anything. The entire point of async in WebAPI is to reduce the number of threads, so you don't want to queue work to the thread pool at all. If this explanation isn't clear, then think about the answer to this question: Why do I want to make it asynchronous?
Can you point me in the direction of some detailed books on this? I'd really like to get into the guts of what is happening and understand this on a more involved level.
@StephenCleary Stephen, would there be no benefit to retain async for the GetNews method and wrap synchronous SomeApi.Search code into Task.Run? Assuming that SomeApi.Search cannot be modified and stays the same, are you suggesting that async keyboards are to be removed from both functions? Thank you.
@mickyjtwin: AFAIK there are no books that specifically address async on the server side. There's a great video on Channel9 but other than that the best resources I know of are my own blog and slide deck (hopefully I'll be posting video in a couple weeks).
|
0

To make function Search truly async the call to SomeApi.Search can be wrapped into a separate task and then awaited. Also consider wrapping the code in first function into a task, with your load estimates cache validation might also be a bottleneck.

// call to 2nd api
var search = await Task.Factory.StartNew(()=> {return Some Api.Search(allFilters, anyFilters, noneFilters, pageSize, pageNumber, exact,
                                               sortBy, null, WebApiConstant.Settings.CustomFields, true);});

5 Comments

Actually, that's "fake asynchronous", not "true asynchronous", and you definitely do not want to do that on the server side (WebAPI). BTW, Task.Run is better than StartNew for async code.
What do you class as true asynchronous then for server side webapi?
I use the term "truly asynchronous" for asynchronous methods that do not queue work to the thread pool. On the server side, the entire point of async is to free up worker threads, and if you're freeing up a worker thread by queueing work to the thread pool (to be executed by another worker thread), you gain nothing. OTOH, this is a useful technique for async on the client, because the point there is to free up the UI thread. So you can queue up work to the thread pool and that's fine as because it's not being done on the UI thread.
@StephenCleary Thanks for your feedback. I will look into the slides you mentioned before I comment on "fake async". In regards to your commend about Task.Run, I think it would be better if you mentioned that Task.Run is just a shorter alternative to Task.Factory.StartNew. The word "better" is just too general statement.
@AlexS: The phrasing I usually use is "Task.Run is preferred over TaskFactory.StartNew for async code. Here's why." But "better" is not incorrect. The problem with StartNew is that it is so easy to use incorrectly, e.g., forgetting the TaskScheduler argument (which should always be specified - some details are in Stephen Toub's blog post).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.