I'm using ServiceStack v4, and making an async request like:
var client = new JsonServiceClient(url);
Task reqTask = client.GetAsync(request);
I'll be making a lot of requests simultaneously to different servers, and need to support cancellation to immediately cancel all outstanding calls. The underlying AsyncServiceClient has a method CancelAsync, but it doesn't seem to do anything.
How can I cancel the request such that it aborts the web request immediately?
Short version, it seems that CancelAsync does nothing indeed.
Looking at the code, it seems that ServiceStack uses the old way (v3) of asynchronously executing requests and notifying you with with callbacks, all built on top of WebRequest and the APM-style calls (BeginXXX,EndXXX).
The GetAsync, SendAsync methods are actually wrappers that use a TaskCompletionSource to provide a Task based interface over the actual asynchronous operation.
CancelAsync actually calls a CancelAsyncFn delegate that isn't set by the classes in JsonServiceClient's hierarchy, so calling it actually does nothing. I suspect this is provided to create custom clients and actions.
In any case, cancelling an HTTP request isn't so easy as it sounds. You need to understand what it is that you are trying to cancel.
You can't actually cancel the call to the server once it's left your application.
You can't tell the server or the intervening devices to stop processing, as HTTP doesn't provide such a mechanism. The server has to provide such an API, which is actually a new HTTP call.
You can't prevent the server from sending a response
You can cancel sending the entire request to the server. This will only work for requests that take a detectable amount of time to serialize
You can ignore the server's response and refuse to open a stream to read the body
You can stop reading the response from the server
You can simply ignore the results of the operation
Looking at the code, it seems that you can't do #4, #5 or #6, because AsyncServiceClient doesn't expose the IAsyncResult used internally. The only thing you can do is simply ignore the response and pay the price of deserialization. Note, the same applies if you use HttpClient's GetStringAsync methods instead of executing each individual step.
So, if cancel means abandoning the request, you can do this by simply ignoring the return value. If it means stop server processing, you can't.
Cancellable requests is a feature.
http://www.github.com/ServiceStack/ServiceStack/wiki/Cancellable-Requests
You basically tag your requests and the feature adds service calls to cancel using the tag. You also have to wrap logic in your service to handle this feature, much like the caching feature.
Related
I have a .NET Core service. We want to implement fire and forget call for one of its endpoint. When request comes to this end point it should fire a method and immediately returns response as Ok. The method then processes that requests by calling other services, and some DB operations.
The above works fine. But, when I tried to do perf testing with thousands of requests, sent back to back, we noticed that some requests are not getting processed at all. It is working fine up to 4000 requests but more than that it is not processing some(approx 20+) requests. There are no exceptions in the logs
How can I identify the issue?
await Task.Factory.StartNew(() => FireAndForgetMethod());
return Ok();
"Fire and forget" literally means that you don't care if the completes and you're perfectly fine with ignoring exceptions. Since you do care that it completes and you do want to see exceptions, then you do not have a situation that is appropriate for "fire and forget".
When request comes to this end point it should fire a method and immediately returns response as Ok. The method then processes that requests by calling other services, and some DB operations.
Your API endpoint should serialize the work to be done into a message and place it on a reliable message queue (e.g., Azure Queue, Amazon Simple Queue, etc), after which it may return an HTTP result to its caller. This queue should then be read and each message executed by a background processor (e.g., ASP.NET Core Worker Service).
We are trying to Consume REST API, for message processor which has some operation which might take more than configured timeout.
Would like to know, if the timeout of Http call to API, will stop execution of API, or API will keep executing?
Idea is that, we can fire and forget API, we are not worried if API does not return 404 or 503. But would like to hear if API will continue to execute?
Any input or suggestion appreciated.
You should use some kind of background processing to handle the process.
I recommend using Hangfire for it.
https://www.hangfire.io/
Use Hangfire to enqueue a job, it will return a job id. You can return this job id to client side.
Expose another API to check for the status of this job.
Great way is to handle this with callback/observer pattern. First of all, understand that there are two types of timeout, server and client. You can explicitly specify client timeout, server timeout is handled by server itself.
So, you will need to implement algorithm such that,
you identify each request unique way and mark it before firing into
memory or file/db.
Fire request with associated callback method.
Hence on response you have control to do stuff like, mark request
fulfilled or failed or what ever it is.
Mark/delete request data.
I have an ASP.NET (webforms) page that needs to call a stored procedure which may take up to a minute to return. I realise that this is not ideal but the the database side of this project is out of my hands and hence I must live with this problem.
Basically I am looking for some method which will allow the page to render without the stored procedure hanging the page - with the results from the database call being displayed when available.
So, I am looking at an async page. I have added "ASYNC=TRUE" to the top of the page and so far, I have the following:
private async void GetCampaignCounts(int CampaignID)
{
Task t = new Task
(
() =>
{
CampaignService cs = new CampaignService();
FilterSet.TargetCounts f = cs.GetCampaignDetails(CampaignID); //LONG RUNNING DB CALL
if (f.Total > 0)
{
panelStatsLeft.Visible = true;
//DO STUFF IN HERE
}
else
panelStatsLeft.Visible = false;
}
);
t.Start();
await t;
}
However, this still hangs the page whilst the database query is running!
Am I doing something totally wrong?!
The asynchronous requests in web applications are not intended to not hang the page. The request will still take the same time as the synchronous version. The benefits are in scalability (i.e. if your page could handle 100 simultaneous users before it would be able to handle 1000 if it is async on the same hardware assuming that the bottleneck was the request pipeline and not the database).
If you want to make the page load and update itself when the operation completes I am afraid you will need significantly more complex architecture. Your best bet is load the page and do an ajax request that runs the query and update the page when the request returns. It is a best practice to use async/await for this ajax request (again for scalability).
Personally i wouldn't bother tying the two together. A better operation is to offload the query elsewhere and return later to get the results.
So use a signalling framework such as signalr. Submit your report parameters, pass them to msmq where they can be handled on a different server or use a one way wcf request. When the request is received optionally store the result in the database and use signalling to notify client (either passing them the actual result or telling them the url where they can download the report from (pick it up from the database in that other url)). Async that into the users current browser page which presumably has a spinner saying "hey we are generating your report".
Consider signalling too.
As I describe in my MSDN article on the topic, async on ASP.NET is not a silver bullet:
When some developers learn about async and await, they believe it’s a way for the server code to “yield” to the client (for example, the browser). However, async and await on ASP.NET only “yield” to the ASP.NET runtime; the HTTP protocol remains unchanged, and you still have only one response per request. If you needed SignalR or AJAX or UpdatePanel before async/await, you’ll still need SignalR or AJAX or UpdatePanel after async/await.
This makes sense if you think about the HTTP protocol: there can be only one response for each request, and async doesn't change the HTTP protocol (more detail on my blog). So, if you return a response to the client (allowing it to render the page), then that request is done. You can't send a followup response later to change the page you already sent.
The proper solution is fairly complex, because ASP.NET wasn't designed to track "background" operations without a request. ASP.NET will recycle your application periodically, and if there are no active requests, it assumes that it's a good time to do so. So, "background" operations are in danger of being terminated without notice.
For this reason, the best solution is to have an independent worker process that actually executes the background operation using a basic distributed architecture (requests are placed into a reliable queue by the ASP.NET handler; requests will re-enter the queue automatically if the worker process fails). This also means your requests should be idempotent.
If you don't want this level of complexity, you can trade-off reliability for complexity. An intermediate step is Hangfire, which requires a database backend at least (which it uses for reliability instead of a queue). If you want the least reliability (and least complexity), you can just use HostingEnvironment.QueueBackgroundWorkItem or my ASP.NET Background Tasks library. I have a longer overview of these options on my blog.
If I need to call a method that in turn calls some async method internally, as a fire and forget operation, how can I prevent this call from forcing the "async" to need to be used up the call stack to say...an MVC controller?
For example: My MVC Controller (not async) calls a business layer method, which in turn calls the Windows Azure Service Bus QueueClient.SendAsync(BrokeredMessage), to drop a message in a queue, but doesn't need to wait for it to complete.
Typically, when this controller action is invoked, the compiler will throw an error that the async operation cannot be started at this time.
I know that instead of awaiting or just invoking the SendAsync() method, I could follow it up with ContinueWith(), in order to execute code on the callback of the async operation, but I've been told this not a correct solution. (see responses to Calling async method in controller)
Would someone care to enlighten me on the best way to fix this scenario? And tell me why the ContinueWith() approach is not correct?
calls the Windows Azure Service Bus QueueClient.SendAsync(BrokeredMessage), to drop a message in a queue, but doesn't need to wait for it to complete.
First, I'd reconsider this assumption. If you want a fully reliable system, the action should wait for the message to be sent to the bus. Note that this is usually a fast operation (100ms).
Typically, when this controller action is invoked, the compiler will throw an error that the async operation cannot be started at this time.
Ensure that you do not have any async void methods or EAP method calls. I would be very surprised if the Azure storage library causes that exception.
Would someone care to enlighten me on the best way to fix this scenario? And tell me why the ContinueWith() approach is not correct?
The best solution is to embrace async. ContinueWith could work but is dangerous to use (it has lots of parameters, some of which have unsafe default values); await is practically the same as ContinueWith but without the dangerous defaults.
However, if 100ms is truly unbearable, and you are willing to give up reliability (in this case, that means you accept the fact that some messages may not get sent to the bus even though the action completed successfully so the client thinks that they did), then you can use the BackgroundTaskManager from my blog to minimize the chance of lost messages.
An MVC controller method handles an HTTP request and sends back the corresponding HTTP response to the client. If I understood your question correctly, you want to call a fire-and-forget method from your async MVC controller method, then proceed with the HTTP response delivery, so the fire-and-forget method doesn't hold the response.
Indeed, you cannot fire it from with your controller method this way, without awaiting its results. I.e., you could, but if your ASP.NET application would be restarted, or the IIS server taken out of the farm, your ContinueWith callback would never be called. So, you woudn't know if the request had ever reached the Windows Azure Service.
One approach to solve this is to run a helper Web API or WCF service on the same host, or on another host on the same network (so the turn-around for a service call would be really quick). It can be a self-hosted service. You'd call this helper service from your MVC controller just to queue the fire-and-forget operation. You'd await the result of this call, but it's not a problem in this case, because both the caller and the callee of this operation would exist on the same network.
This way, the original MVC HTTP response won't be put on hold. Inside the helper service, you'd then do await QueueClient.SendAsync() to call Windows Azure Bus and process the result of this operation accordingly.
I'm trying to extend WebAPI to support returning a response through an HTTP callback.
Workflow:
WebAPI receives a HTTP request with a callback URL.
WebAPI handles the URL normally and if the operation completes in less time than a configured timeout the result is sent synchronously.
If the timeout is exceeded the server needs to send an HTTP response indicating it went async, processing continues.
When processing (eventually) completes the response of the controller is posted to the pre-negotiated callback url.
Controllers need to remain synchronous and unaware of the async/callback functionality.
It appears MessageHandlers are a likely candidate but returning multiple HTTP responses (one for the early 'long task' response and one for the callback) does not appear to be supported.
Can someone provide guidance on what areas of WebAPI are extensible and relevant to this scenario?
I think an HttpMessageHandler will do the trick but not the way I think you're asking for.
One URL will be the main one and will return either the result or the redirection and the other will handle the redirections.
This is a very common scenario. In some cases you'll ask for a list of something and receive the a managed amount of results and a continuation URL if there are more. You requirement might be looked up as being just that where you either only have a continuation or the whole results.
Another way of looking at it as CQRS (Command Query Responsibility Segregation). You issue a command to on URL and retrieve the response from another. As an optimization, the result of invoking the command might be the response instead of the query URL.
Does this help you?