Here is my scenario.
I want to create a web endpoint that will kick off a service that may be long-running, perhaps 20-30 minutes. I'd like to return an HttpResponseResult immediately and trigger the service to run asynchronously, instead of having to wait for the service to complete before returning a response to client.
What is the easiest way to go about doing this? I don't need to return any sort of result from the service, just trigger it.
Just run a task using Task.Run(() => <trigger service call> ) and ignore the return value. The only down side of this is that it will consume a thread from the thread pool. If the service has an asynchronous version of the operation you are calling you can use a TaskCompletionSource
SignalR will be my choice to do this kind of behavior. Tutorial is here.
Basically, a client invokes a server method/action and is 'done' (you can continue and do whatever y you want in the client side). Once the server is done it pushes data/notification to client via RPC.
On the server side you can execute the code anyway you like, synchronously or async.
Related
I am new to C# and working on a Web Service project. I am having a scenario as below :
1. From web i will get the parameters
2. With the request i construct and start a set of test cases ,
this is time taking and user may not wait for almost 40 mins to an hour.
Instead Can i send the response back to the browser to say that the execution is initiated ? and as a background thread will run the next task.
Any idea or info will be of great help. I will have to use C# and ASP.net as my api's for UCMA are in c#. Basically i want to do something like:
var threads = new List<Thread>
{
new Thread(() => sendResponse(fileLogger,execution)),// expecting this to send the response back to browser.
new Thread(() => goAheadWithlongerTimetakingTask(fileLogger))
};
threads.ForEach(t => t.Start());
threads.ForEach(t => t.Join());
Once the second thread is done i would kill both the threads in Finally.
If this is not a better solution, can you suggest a better one ?
Don't mess with threads in ASP.NET.
Instead, make the initial request schedule a task with a tool like HangFire or Quartz.NET. Then, have a Windows Service running that knows to watch for those scheduled tasks and execute them. Allow the web client (browser) to poll (or, if you want to get fancy, use something like SignalR) to check the status of the task, and when it's done retrieve the result.
Scott Hanselman has a great post about different strategies you can use for running background tasks in ASP.NET here. I recommended the Hangfire/Quartz.NET strategy because the 40-minutes-to-1-hour timespan you're talking about is easily enough time for ASP.NET to get shut down while you're trying to execute the background task, so you'll definitely want to offload that to a separate app.
I am trying to implement a task in fire and forget manner.
Lets look at the below piece of code.
public IHttpActionResult Update(int id)
{
var updatedResult = _updater.update(id);
// fire and forget a task
sendEmailToUser();
return ok();
}
private async Task sendEmailToUser()
{
var httpclient = new HttpClient();
// assume the client is initiated with required url and other headers
await httpclient.postasync("some url");
}
Given the above code, can i safely assume that whenever Update endpoint is called, sendEmailToUser task is triggered and will be run to completion ?
No. You should almost never start any background threads in web application. HTTP is suppose to be stateless and the web server was designed with that in mind.
The server might be put into sleep state when there is no incoming request for a set period of time. During that time all the background execution will be halt including the one you had. It might and might not get resume when the next request comes in.
Or when IIS decides to recycle your App domain on a scheduled basis your thread will get killed too.
If you really need background tasks then do that using windows service or run it as a separate console application.
Under normal conditions, it's reasonable to expect that the task will run to completion. It will go on independently.
Your biggest concerns, in this case, should be about the web API not being terminated, and the task not throwing an exception.
But if OP needs to be 100% sure, there are other safer ways to code that.
I am developing a web-api that takes data from client, and saves it for later use. Now i have an external system that needs to know of all events, so i want to setup a notification component in my web-api.
What i do is, after data is saved, i execute a SendNotification(message) method in my new component. Meanwhile i don't want my client to wait or even know that we're sending notifications, so i want to return a 201 Created / 200 OK response as fast as possible to my clients.
Yes this is a fire-and-forget scenario. I want the notification component to handle all exception cases (if notification fails, the client of the api doesn't really care at all).
I have tried using async/await, but this does not work in the web-api, since when the request-thread terminates, the async operation does so aswell.
So i took a look at Task.Run().
My controller looks like so:
public IHttpActionResult PostData([FromBody] Data data) {
_dataService.saveData(data);
//This could fail, and retry strategy takes time.
Task.Run(() => _notificationHandler.SendNotification(new Message(data)));
return CreatedAtRoute<object>(...);
}
And the method in my NotificationHandler
public void SendNotification(Message message) {
//..send stuff to a notification server somewhere, syncronously.
}
I am relatively new in the C# world, and i don't know if there is a more elegant(or proper) way of doing this. Are there any pitfalls with using this method?
It really depends how long. Have you looked into the possibility of QueueBackgroundWorkItem as detailed here. If you want to implement a very fast fire and forget you also might want to consider a queue to pop these messages onto so you can return from the controller immediately. You'd then have to have something which polls the queue and sends out the notifications i.e. Scheduled Task, Windows service etc. IIRC, if IIS recycles during a task, the process is killed whereas with QueueBackgroundWorkItem there is a grace period for which ASP.Net will let the work item finish it's job.
I would take a look on Hangfire. It is fairly easy to setup, it should be able to run within your ASP.NET process and is easy to migrate to a standalone process in case your IIS load suddenly increases.
I experimented with Hangfire a while ago but in standalone mode. It has enough docs and easy to understand API.
I'm trying to implement a functionality where there is a stored procedure on SQL Server that has to be called from ASP MVC application and processed on the background (it might take long since it calls another stored procedure remotely to process an excel file stored on a server). But the response of the last HTTP request should be returned back to client so the UI will not be hanging waiting for processing.
I have tried so many different ways but UI is still not responding right away.
I tried BackgroundWorker but it's not allowing the main thread to response back to client until its done processing,
also I tried:
Thread.QueueUserWorkItem(delegate { //method which performs stored procedure calls//
});
It still not returning response and HttpContext.Current not available in background thread.
Maybe there is a way to start background processing, pause it for letting main thread to return response to browser and then resume background thread to make all processing with stored procedure calls?
Am I missing something?
Could someone please give an idea how I can solve this problem? Would be much appreciated.
What I ended up with and it works fine in my case. I'm not sure about efficiency, but it perfectly works. So, the code which makes calls to a stored procedure I put on a separate thread, so that the main thread is finished while the processing off background calls is happening on a separate thread and finishes successfully after some period of time. At the same time UI is available so user can make another request which will also be processed the same way. I tested three requests. One after another overlapping, meaning that while first request was being processed on the background I submitted another one and yet another one. UI was responding immediately and all the work was done.
// ...main thread is working here
//put a call to stored procedure on a separate thread
Thread t = new Thread(()=> {
//call stored procedure which will run longer time since it calls another remote stored procedure and
//waits until it's done processing
});
t.Start();
// ...main thread continue to work here and finishes the request so it looks for user as the response is coming right away, all other stuff is being processed on that new thread so user even doesn't suspect
I shall quote Stephan Clearys great article:
When you use async on the server side (e.g., with ApiController), then you can treat each web request as an asynchronous operation. But when you yield, you only yield to the web server thread pool, not to the client. HTTP only allows a single response, so the response can only be sent when the request is fully complete.
Basically, this doesn't adhere to the HTTP protocol, where each request has only one response.
This can be achieved using multiple calls to the ASP.NET service, where a request returns a unique ID immediately, which the client can query multiple times for progress. You may look into SignalR for help with such an implementation:
What is SignalR and "real-time web" functionality? It's the ability to have your server-side code push content to the connected clients as it happens, in real-time.
There is part1 and part2 article by Dino Esposito outlines a way to achieve your polling using a client side timer and controller actions. You would basically serialize access to a progress worker controller method that returns task status and completion data. However, it may be a little chatty if you are only going to be performing one or two long running processes.
If the response to the client does not depend on the result of the background process (i.e. run the process in the background and don't have the UI waiting for it), then you could use Revalee (an open-source tool) to perform the background task.
The UI will request this route...
public class ForegroundController : Controller
{
public ActionResult InitiateBackgroundTask()
{
// The absolute URL that will be requested on the callback.
var callbackUri = new Uri("http://localhost/BackgroundTask/Callback");
// The information that will be needed to initiate the background task.
object state = "Any object";
this.CallbackNowAsync(callbackUri, state)
// ... your controller will now return a response to the browser
return View();
}
}
Background work will be executed in this controller...
public class BackgroundTaskController : Controller
{
[AllowAnonymous]
[HttpPost]
[CallbackAction]
public ActionResult Callback(Guid callbackId, object state)
{
// Perform the background work
// ... insert your background task code here ...
// Return a status code back to the Revalee Service.
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
}
Revalee Project Site
I have a WCF service set to PerCall
I would like to know how I can send a Start call from the client to start a long running process, and send a Cancel command to cancel it
My WCF service looks something like this
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service1 : IService1
{
CancellationTokenSource cancelToken = new CancellationTokenSource();
public void Start()
{
var compute = Task.Factory.StartNew(StartLongRunningTask, cancelToken.Token);
}
public void Stop()
{
cancelToken.Cancel();
}
private void StartLongRunningTask()
{
//process here
}
}
I guess the problem here is that, each time a call comes to the server, it's treated as a new request.
So how should starting and cancelling a long running task in WCF be done?
EDIT: I'm hosting it as a windows service
I have a WCF service set to PerCall
... the problem here is that, each time a call comes to the server, it's treated as a new request.
Yup, that's exactly what you're telling it to do. If you can, just change to InstanceContextMode.PerSession; then you can do what you're trying to do (assuming you're self-hosting).
If you can't do this, then you'll have to develop a more complex solution like #PeterRitchie commented. First, your host: IIS is not designed to have long-running operations independent of requests, so I'll assume you're self-hosting. Next, you'll need a form of token (like a GUID) that will act as an identifier for a long-running operation. Your Start method will allocate a GUID and CancellationTokenSource and start the operation, and your Stop method will take a GUID and use that to look up the CancellationTokenSource and cancel the operation. You'll need a shared (static, threadsafe) dictionary to act as lookup.
If your host is IIS, then your solution gets more complex... :)
First, you'll need a backend that's not hosted in IIS. Common choices are an Azure worker role or a Win32 service. Next, you'll need a reliable communications mechanism: an Azure queue, MSMQ, WebSphere, etc. Then you can build your WCF-over-IIS service to have the Start method generate a GUID identifier and drop a message on the queue to start processing. The Stop method takes the GUID and drops a message on the queue to cancel processing. All other logic gets moved to the backend service.
From how you've asked, the client seems to be aware of the async nature of the request.
#StephenCleary and #PeterRitchie's points are excellent, but your first step is to re-do your service/contract to properly implement an async service and add the means of communicating back (to client) some information/handle to the long running operation.
The Framework contains several paradigms for asynchronous programming (already :-) )but when it comes to WCF, you kinda fall back to How to: Implement an Asynchronous Service Operation
That will provide some infrastructure, but not necessarily the ability to automatically cancel an operation.
Speaking strictly about the cancellation (as this is your question): you will have to extend whatever your solution ends up being for cancellation. At the minimum you need to add necessary logic to your service “worker” to monitor and honor the cancellation token.
Other considerations that you may expect to encounter: return result from cancellation; cancelling a task that has managed to complete (what of you updated the 1,000,000 records by the time the cancellation request came); exception handling (with task-based programming exceptions are not thrown, but bundled in the Task, or whatever other “vehicle” you use to describe the ongoing operation).