Wrapping both cpu-bound/io-bound long-running code into (async) Task - c#

Consider the simple MVC5 controller:
public class DocumentsController {
// ctor code is omitted
[HttpPost, Route("api/documents/request/stamp={stamp}")]
public ActionResult RequestDocuments(string stamp) {
var documents = this.DocumentsRequestService.RequestByStamp(stamp);
return new JsonResult(documents);
}
}
The DocumentsRequestService does these things internally:
it sends a request to a dedicated MSMQ-queue (let's call it M) AND synchronously waits for an incoming message at the M's response queue:
using(var requestMessage = new Message()) {
requestMessage.Body = documentStamp;
requestMessage.Recoverable = true;
requestMessage.Label = "request";
requestMessage.ResponseQueue = this.requestedDocumentsResponseQueue;
requestMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(String) });
// send request
this.requestedDocumentsQueue.Send(requestMessage);
// synchronously wait for response
var responseMessage = this.requestedDocumentsResponseQueue.Receive();
if(responseMessage.Label.EndsWith("success")) {
return new DocumentsRequestResult(
success: true,
matches: parseMatchesList(responseMessage)
);
}
return new DocumentsRequestResult(
success: false,
matches: Enumerable.Empty<DocumentsRequestMatch>()
);
}
the consumer (Windows Service) of that message makes a specific api call. By saying 'specific' I mean that we use a third-party means to do that. This call is synchronous and quite long. When the processing ends the consumer sends a response message to the requesting message's response queue.
when response arrives at the M's response queue it's a time to parse and return the results to the controller.
From the end user's perspective this task should be blocking, or at least it should look like blocking.
As far as I understand running a Task makes use of parallelization. Whereas using the async-await pair makes the running task asynchronous. It could be helpful if several tasks would run in parallel.
Is it reasonable/possible to incorporate with Tasking/Asynchrony in my case? If yes, then where do I start?

The "asynchrony" of a network call is transparent to the caller. It doesn't matter to the caller whether the implementation is synchronous or asynchronous. Put another way, from a client's perspective, it's always asynchronous.
For example, the HTTP client couldn't care less if RequestDocuments is synchronous or asynchronous; either way, the HTTP client will send a request and receive a response some time later (i.e., asynchronously).
Similarly, the HTTP web server doesn't care whether the Win32 service is implemented synchronously or asynchronously. It just knows that it puts a message on a queue and some time later (i.e., asynchronously) it gets a response message from the queue.
As far as I understand running a Task makes use of parallelization. Whereas using the async-await pair makes the running task asynchronous.
Sort of. Task can be used for either asynchronous or parallel code, a fact that has caused much confusion. However, Task Parallel Library constructs such as Parallel and PLINQ are firmly in the parallel (non-asynchronous) world.
It could be helpful if several tasks would run in parallel.
I believe "concurrently" is the appropriate term here.
First, note that ASP.NET gives you a considerable amount of concurrency for free. If you want to make each request internally concurrent, then you can do so fairly easily via Task.WhenAll. For example, you can change your DocumentsRequestService call to be asynchronous (assuming your message queue API supports async calls):
using(var requestMessage = new Message()) {
...
// send request
await this.requestedDocumentsQueue.SendAsync(requestMessage);
// asynchronously wait for response
var responseMessage = await this.requestedDocumentsResponseQueue.ReceiveAsync();
...
}
Then you can call it multiple times simultaneously from a single controller action as such:
public async Task<ActionResult> RequestDocuments(string stamp1, string stamp2) {
var task1 = this.DocumentsRequestService.RequestByStampAsync(stamp1);
var task2 = this.DocumentsRequestService.RequestByStampAsync(stamp2);
var documents = await Task.WhenAll(task1, task2);
return new JsonResult(documents);
}

Related

Is it possible for HTTP server to receive requests out of order even if they are sent sequentially?

(This discussion might not be specific to C#...)
I have a C# method SendMultipleRequests that sends HTTP POST request 10 times sequentially.
Is it possible for the server to receive requests out of order?
If my understanding is correct if the requests are sent concurrently (without await), the server could receive requests out of order, but in the example below it needs to wait for the response to be received at the client before sending next request, so the server will receive requests in order.
public async Task SendRequest(int i)
{
// definition of endpoint is omitted in this example
var content = new StringContent($"I am {i}-th request");
await HttpClient.PostAsync(endpoint, content);
}
public async Task SendMultipleRequests()
{
for (int i = 0; i < 10; i++)
{
await SendRequest(i);
}
}
with await your app will wait for the task returned by PostAsync to finish before it issues the next request - see the docs for postasync where it says “This operation will not block. The returned Task<TResult> object will complete after the whole response (including content) is read.” - using await will mean that you will only issue the next request after you I’ve read the content of the previous response
If you remove the await then your code will queue up ten tasks and start working on them all in some undefined order. The server will see requests in an unspecified order. This may be further exacerbated by the fact that the requests may take different routes through the internet, some slower. If you remove the await then you should capture the returned task into a list, then you can use something like await Task.WhenAll(list) to wait for them all to complete (unless you really want to fire and forget in which case you can assign them to the discard _ = client.PostAsync... but keeping the task allows you to discover and deal with exceptions that arose)

What is the benefit to using await with an async database call

I am just looking at the default MVC5 project and how it uses async in the controllers.
I would like to know what benefit async provides here over simply using synchronous calls:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
{
ManageMessageId? message = null;
//why use an async database call here with await instead of just using a synchronous one?
IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
message = ManageMessageId.Error;
}
return RedirectToAction("Manage", new { Message = message });
}
What am I missing?
Does this provide some kind of performance benefit in the type of wait that will occur here?
On the server side (e.g., ASP.NET MVC), any I/O you do (e.g., databases) should be done asynchronously. This frees up the request thread for the time that the I/O is in flight.
So, when the RemoveLoginAsync sends its SQL to the database, it returns an incomplete task, and when the request hits the await, it returns the request thread to the thread pool. Later, when the DB operation completes, a request thread is taken from the thread pool and used to continue the request.
The end result is scalability, because (in the 99.9% case at least) tasks scale better than threads. However, there isn't a big advantage if your database is just a single server and all requests hit the db, because your scalability bottleneck in that scenario is the db server, not the web server.

How can I POST an HTTP request and wait for a callback without blocking the current thread?

We have a .NET application that uses an HTTP based API where we POST a request to a third party HTTP endpoint (that is not under our control) and it calls us back at a later time on an HTTP endpoint that we give it; something like:
WebRequest request = WebRequest.Create(urlToMethod);
request.Method = #"POST";
request.Headers.Add(#"Callback", "http://ourserver?id="+id );
We make thousands upon thousands of these calls and so we'd like to be as effecient as possible (in terms of speed/memory/threads etc.)
As far as the callback code is concerned, we have a type that acts as a listener; this is how we start it up:
_httpListener = new HttpListener();
_httpListener.Prefixes.Add(ourServer);
_httpListener.Start();
_httpListener.BeginGetContext(callback, null);
When the server calls us back, it hits our callback method which looks something like this:
HttpListenerContext context = _httpListener.EndGetContext(result);
HttpListenerResponse httpListenerResponse = context.Response;
httpListenerResponse.StatusCode = 200;
httpListenerResponse.ContentLength64 = _acknowledgementBytes.Length;
var output = httpListenerResponse.OutputStream;
output.Write(_acknowledgementBytes, 0, _acknowledgementBytes.Length);
context.Response.Close();
var handler = ResponseReceived;
if (handler != null)
{
handler(this, someData);
}
So we have a single instance of this listener (_which internally uses HttpListener) and for every response it gets, it informs all of the subscribers on the ResponseReceived event.
The subscribers (possibly hundreds of them) only care about data associated with their particular id. The subscribers look something like:
_matchingResponseReceived = new ManualResetEventSlim(false);
_listener.WhenResponseReceived += checkTheIdOfWhatWeGetAndSetTheEventIfItMatches;
postTheMessage();
_matchingResponseReceived.Wait(someTimeout);
It's that last line that's bugging me. We post the message but then block the whole thread waiting for the Listener to get a response and call our event handler. We'd like to use Tasks but doesn't seem like it'll give us much if we're blocking a whole thread waiting for the callback.
Is there a better (more TPL friendly) way of achieving this so that no threads are blocked and we get fire off more requests simultaneously?
async-await together with TaskCompletionSource pretty much were made for this.
The sender side creates a TaskCompletionSource, adds it to a dictionary (with key being the id of the request), makes the request and returns the TaskCompletionSource's Task.
The receiver then looks into the dictionary to find the right TaskCompletionSource, removes it from there and sets its result.
The caller of the sender method will await the returned Task, which will asynchronously wait for the receiver to process the callback call.
In code, it could look something like this:
// TODO: this probably needs to be thread-safe
// you can use ConcurrentDictionary for that
Dictionary<int, TaskCompletionSource<Result>> requestTcses;
public async Task<Result> MakeApiRequestAsync()
{
int id = …;
var tcs = new TaskCompletionSource<Result>();
requestTcses.Add(id, tcs);
await SendRequestAsync(id);
return await tcs.Task;
}
…
var result = await MakeApiRequest();
var context = await _httpListener.GetContext();
// parse the response into id and result
var tcs = requestTcses[id];
requestTcses.Remove(id);
tcs.SetResult(result);
This whole architecture seems to be more complicated than it should be (I might have not understood your program right).
Why not post your request to the second server (BTW, you don't need string literal for "POST") and end the routine, then get the request from that server in a regular Web API method, parse the data to find the IDs, and execute thread for each ID?

Web Api + HttpClient: An asynchronous module or handler completed while an asynchronous operation was still pending

I'm writing an application that proxies some HTTP requests using the ASP.NET Web API and I am struggling to identify the source of an intermittent error.
It seems like a race condition... but I'm not entirely sure.
Before I go into detail here is the general communication flow of the application:
Client makes a HTTP request to Proxy 1.
Proxy 1 relays the contents of the HTTP request to Proxy 2
Proxy 2 relays the contents of the HTTP request to the Target Web Application
Target Web App responds to the HTTP request and the response is streamed (chunked transfer) to Proxy 2
Proxy 2 returns the response to Proxy 1 which in turn responds to the original calling Client.
The Proxy applications are written in ASP.NET Web API RTM using .NET 4.5.
The code to perform the relay looks like so:
//Controller entry point.
public HttpResponseMessage Post()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
private static HttpRequestMessage BuildRelayHttpRequest(HttpRequestMessage incomingRequest)
{
var requestUri = BuildRequestUri();
var relayRequest = new HttpRequestMessage(incomingRequest.Method, requestUri);
if (incomingRequest.Method != HttpMethod.Get && incomingRequest.Content != null)
{
relayRequest.Content = incomingRequest.Content;
}
//Copies all safe HTTP headers (mainly content) to the relay request
CopyHeaders(relayRequest, incomingRequest);
return relayRequest;
}
private static HttpRequestMessage BuildResponse(HttpResponseMessage responseMessage)
{
var returnMessage = Request.CreateResponse(responseMessage.StatusCode);
returnMessage.ReasonPhrase = responseMessage.ReasonPhrase;
returnMessage.Content = CopyContentStream(responseMessage);
//Copies all safe HTTP headers (mainly content) to the response
CopyHeaders(returnMessage, responseMessage);
}
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
var content = new PushStreamContent(async (stream, context, transport) =>
await sourceContent.Content.ReadAsStreamAsync()
.ContinueWith(t1 => t1.Result.CopyToAsync(stream)
.ContinueWith(t2 => stream.Dispose())));
return content;
}
The error that occurs intermittently is:
An asynchronous module or handler completed while an asynchronous operation was still pending.
This error usually occurs on the first few requests to the proxy applications after which the error is not seen again.
Visual Studio never catches the Exception when thrown.
But the error can be caught in the Global.asax Application_Error event.
Unfortunately the Exception has no Stack Trace.
The proxy applications are hosted in Azure Web Roles.
Any help identifying the culprit would be appreciated.
Your problem is a subtle one: the async lambda you're passing to PushStreamContent is being interpreted as an async void (because the PushStreamContent constructor only takes Actions as parameters). So there's a race condition between your module/handler completing and the completion of that async void lambda.
PostStreamContent detects the stream closing and treats that as the end of its Task (completing the module/handler), so you just need to be sure there's no async void methods that could still run after the stream is closed. async Task methods are OK, so this should fix it:
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
Func<Stream, Task> copyStreamAsync = async stream =>
{
using (stream)
using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
{
await sourceStream.CopyToAsync(stream);
}
};
var content = new PushStreamContent(stream => { var _ = copyStreamAsync(stream); });
return content;
}
If you want your proxies to scale a bit better, I also recommend getting rid of all the Result calls:
//Controller entry point.
public async Task<HttpResponseMessage> PostAsync()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
Your former code would block one thread for each request (until the headers are received); by using async all the way up to your controller level, you won't block a thread during that time.
I would like to add some wisdom for anyone else who landed here with the same error, but all of your code seems fine. Look for any lambda expressions passed into functions across the call-tree from where this occurs.
I was getting this error on a JavaScript JSON call to an MVC 5.x controller action. Everything I was doing up and down the stack was defined async Task and called using await.
However, using Visual Studio's "Set next statement" feature I systematically skipped over lines to determine which one caused it. I kept drilling down into local methods until I got to a call into an external NuGet package. The called method took an Action as a parameter and the lambda expression passed in for this Action was preceded by the async keyword. As Stephen Cleary points out above in his answer, this is treated as an async void, which MVC does not like. Luckily said package had *Async versions of the same methods. Switching to using those, along with some downstream calls to the same package fixed the problem.
I realize this is not a novel solution to the problem, but I passed over this thread a few times in my searches trying to resolve the issue because I thought I didn't have any async void or async <Action> calls, and I wanted to help someone else avoid that.
A slightly simpler model is that you can actually just use the HttpContents directly and pass them around inside the relay. I just uploaded a sample illustrating how you can rely both requests and responses asynchronously and without buffering the content in a relatively simple manner:
http://aspnet.codeplex.com/SourceControl/changeset/view/7ce67a547fd0#Samples/WebApi/RelaySample/ReadMe.txt
It is also beneficial to reuse the same HttpClient instance as this allows you to reuse connections where appropriate.

Limiting asynchronous requests without blocking

i am after some advice/strategy on limiting http requests when consuming multiple web services. I feel i could do this if the requests were happening synchronously, but they are asynchronous and think i should try to perform the limit logic in a way that i wont block.
Due to the web app consuming multiple web services there will be different limits for different requests. I was thinking something like this, but aren't sure how to proceed in a no blocking manner:
request method:
public static Task<string> AsyncRequest(string url, enum webService)
{
using(LimitingClass limiter = new LimitingClass(webService))
{
//Perform async request
}
}
In the LimitingClass it will have logic like checking the last request for the given webservice, if it violates the limit then it will wait a certain amount of time. But in the mean time if another request comes in to a different webservice then i dont want that request to be blocked while the LimitingClass is waiting. Is there anything fundamentally wrong with the above approach? Should i open up a new thread with each LimitingClass instance?
Some pseudo code would be great if possible.
Many Thanks
UPDATE:
This is a simplified version of my current request method:
public static Task<string> MakeAsyncRequest(string url, string contentType)
{
HttpWebRequest request = //set up my request
Task<WebResponse> task = Task.Factory.FromAsync(
request.BeginGetResponse,
asyncResult => request.EndGetResponse(asyncResult),
(object)null);
return task.ContinueWith(t => ReadCallback(t.Result));
}
I just want to wrap this in a using to check the limits and which doesnt block other requests.
So how are you limiting an access to resource without blocking ?
I think you need to look at Semaphores - they allow to limits the number of threads that can access a resource or pool of resources concurrently.

Categories