I've been given a Perl script implementation of a task by a third party product vendor. My job is to translate it into my ASP.NET MVC5 web application.
One component of the perl script is a while loop with this in it:
### Only run this task every second, that's plenty
sleep 1;
Basically they're doing stuff in a while loop at 1 second intervals (pseudo code):
While (condition)
{
-go get an m3u8 playlist file from the web
-parse it line by line
-look for a particular thing in it
-If you find it, break out of this loop and do stuff with it
}
It typically takes between 20-30 tries (20-30 seconds) to find the thing.
Is this the kind of thing asynchronous programming with async and await is geared toward or is this something one just shouldn't have in a web app?
The process would be exposed via ajax call to Web API. If it is feasible, could someone provide some pseudo code on how it might work without blocking the app?
If it is feasible, could someone provide some pseudo code on how it might work without blocking the app?
AJAX calls are always nonblocking. There's nothing special you need to do on the server side for this.
I would recommend using async/await, just so that you don't use up an ASP.NET thread sleeping:
public async Task<MyResult> GetThing()
{
While (condition)
{
-go get an m3u8 playlist file from the web
-parse it line by line
-look for a particular thing in it
-If you find it, break out of this loop and do stuff with it
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
But note that the async is only used to free up a thread on the ASP.NET server side. It has no effect on the client whatsoever. The client makes the AJAX call, and asynchronously handles completion, regardless of the server's implementation.
you can use async/await, but thats more about synchroizing async behaviour. Your problem is a little different, you have a long running process which you can't block on, so you need a way to trigger it to start, providing updates, and then handling completion. You could use something like signalR for this
If you want to use ajax for this, then something like for your API...
StartJob() -> return GUID
JobStatus(guid) -> either, InProgress ( % complete if you can), Final Result, or Error
then client side, Start it, then poll for status until complete.
Implementation wise, you just Start a task and store it in a cache associated with a GUID.
If you want to get fancy pants, you could use something like Microsoft Orleans or Akka to do the processing.
Related
We are developing a monolithic web application – very stateful. It handles both HTTP requests and long lived SignalR connections. (In ASP.NET Core 3.1 – we will upgrade to .NET 5 later.)
We do a redirect from a login page to our “main page”. The main page takes a while to load and initialize, after that it connects with SignalR. We also have a lot of work to do at the server side. Doing the server work in the login request (before redirecting to the main page) would slow down the login.
“Oh, let’s use a Task then!”, I thought. That is, put the server work in a Task, save that in the user state, and let it execute in parallel with the loading of the main page. Something like this (simplified):
public static async Task ServerSideInit()
{
// do a lot of init work
}
// at the end of the controller handling the login page POST:
UserState.BackgroundTask = ServerSideInit();
Redirect(UrlToTheMainPage);
// when the main page connects via SignalR:
try {
await UserState.BackgroundTask;
}
catch {
// handle errors in the init work
}
This would really speed things up. It won’t matter if the page loading or the init work finishes first – we await the Task. And the work in ServerSideInit() isn’t critical. If something happens and the main page never connects, the UserState (and the Task) will be destroyed after a timeout – and that’s perfectly OK. (There are some caveats. We would e.g. have to use IServiceProvider to create/dispose a scope in ServerSideInit(), so we get a scoped DbContext outside of the controller. But that’s OK.)
But then I read that there is a risk the ASP.NET Core framework shuts down the Task when wrapping up the POST request! (Do you have to await async methods?) The simple HostingEnvironment.QueueBackgroundWorkItem isn’t available any longer. There is a new BackgroundService class, though. (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio) But registering a service and queueing jobs seems like a very cumbersome solution… We just want to fire a task that will take a couple of seconds to complete, and let that continue to run after ASP.NET Core has finished handling the POST request.
I’m not very experienced with ASP.NET Core… So I’d be very grateful for some input! Will my simple solution not work? Will the task be terminated by the framework? Is there some easier way to tell the framework “please don’t touch this Task”? Or is BackgroundService the way to go?
Doing the server work in the login request (before redirecting to the main page) would slow down the login. “Oh, let’s use a Task then!”, I thought. That is, put the server work in a Task, save that in the user state, and let it execute in parallel with the loading of the main page.
So, you have a need for request-extrinsic work. I.e., work that your server does that is outside the scope of a request.
The first question you need to ask yourself is "does this work need to be done?" In other words, "am I OK with occasionally losing work?". If this work must be done for correctness reasons, then there is only one real solution: asynchronous messaging. If you're OK with occasionally losing work (e.g., if the main page will detect that the ServerSideInit is not done and will do it at that time), then what you're really talking about is a cache, and that's fine to have an in-memory solution for.
But then I read that there is a risk the ASP.NET Core framework shuts down the Task when wrapping up the POST request!
The first thing to recognize is that shutdowns are normal. Rolling updates during regular deployments, OS patches, etc... Your web server will voluntarily shut down sooner or later, and any code that assumes it will run forever is inherently buggy.
ASP.NET Core by default will consider itself "safe to shut down" when all requests have been responded to. This is the reasonable behavior for any HTTP service, and this logic extends to every HTTP framework, regardless of language or runtime. However, this is clearly a problem for request-extrinsic code.
So, if your code just starts a task by calling the method directly (or by Task.Run, another sadly popular option), then it is living dangerously: ASP.NET has no idea that request-extrinsic code even exists, and will happily exit when requested, abruptly terminating that code.
There are stopgap solutions like HostingEnvironment.QueueBackgroundWorkItem (pre-Core) and IHostedService / IHostApplicationLifetime (Core). These register the request-extrinsic code so that ASP.NET is aware of it, and will not shut down until that code completes. However, those solutions only go partway; since they are in-memory, they are also dangerous: ASP.NET is now aware of the request-extrinsic code, but HTTP proxies, load balancers, and deployment scripts are not.
Is there some easier way to tell the framework “please don’t touch this Task”?
Back to the question at the beginning of this answer: "does this work need to be done?"
If it's just an optimization and doesn't need to be done, then just firing off the work with a Task.Run (or IHostedService) should be sufficient. I wouldn't keep it in UserState, though, since Tasks aren't serializable.
If the work needs to be done, then build an asynchronous messaging solution.
I am making a asp.net webapi call that posts some data to the server and will need to be processed. The client does not need to wait for the processing to finish. I would like to return something like this
HttpResponseMessage objReturn = Request.CreateResponse(HttpStatusCode.Ok);
//start a thread to do some work processing the data
//return while the data is being processed
return objReturn;
Most of the example I find are about how to use async methods and wait for the processing to complete. I need the opposite.
thanks for you suggestions.
more code for those asking, the following code gives me a warning that the method lacks await and will run synchronously.
public async Task<HttpResponseMessage> Post()
{
HttpResponseMessage objReturn = Request.CreateResponse(HttpStatusCode.Ok);
//data processing logic
//something longer running that the client doesnt need to wait for
//like converting a pdf to jpg or other I/O operations
return objReturn;
}
If I read your question correctly, you want a user to call an API, quickly receive a response, and have that trigger a longer running task.
In general, you do not use Web Api to run this longer task, instead yo use a service (i.e. Windows Service).
That service will sit there ... waiting for work ...
your Api will give it work! (Using a database, queues, files, etc.)
However, depending on how important this is, how much effort, and how much time ... you may not want to create a whole separate service. There are some "tools" that can help you.
QueueBackgroundWorkItem
http://hangfire.io/
^^ They will help you run long tasks in your Api directly! ^^
The warning explains most of your problems. Just decorating a method with async does not mean that it runs asynchronous automatically. If you don't have any asynchronous work in your data processing logic it will run synchronously. Event if you have some asynchronous calls in there, the compiler can decide to run it synchronously if it think that's the better option. Remember that asynchronous work does NOT involve another thread.
Some hints what you can do. First, you should make your I/O calls asynchronous. The .NET framework offers a lot you can use here. Second, you should not do that work in a controller. A controller should be small and don't do heavy processing, because it is your communicator to the rest of the world. Pass everything that needs more processing to a queue where a worker role (such as a Windows Service) picks up the work that needs to be done. With that the controller has nothing to do as passing data to the queue, give a result to the client that it was put into the queue ... and done. After that your controller can pick up additional work.
In my scenario when ever user changes some fields in the program and does a SAVE, a webserivce request is sent to save some logging information into the database. I did some search on the website and found this solution for Async calls:
ThreadPool.QueueUserWorkItem(delegate
{
// Create an instance of my webservice object.
// call its Log webmethod.
});
But since I don't have much experience with webservices and Async calls so I wanted to show you the scenario I have and the way I am handling it to get your opinion about it and if it the right way to do this. Thanks for suggestions.
Can you tolerate the logging work to be lost? Only then should you start "background work" in an ASP.NET app.
QueueUserWorkItem will work. The more modern version is Task.Run. Make sure you catch errors that happen on that thread-pool thread. If not, you'll never find out about bugs and silently lose work.
If you expect a high volume of such calls, or expect them to take a long time, consider using async IO. It does not use any thread while in progress (not even a background thread).
I am currently implementing an HTTP Handler that will take a POST of some XML data from some 3rd party, then will do some work with it. The processing that I will be doing with the XML has potential to take some time. Once I yank out the XML from the POST, there is no need to keep the connection open with the client while I process the data. As I don't have any control of when the client will time out posting to me, I just want to grab the XML and let the connection go.
Is there any easy way to go about this? Using the Response.Close() isn't correct, as it doesn't close the connection properly. Response.End() exits my HTTP Handler all together. I could throw the processing into a background thread, but I heard that can be a little risky in ASP.NET as the AppDomain can be torn down which could kill my process in the middle.
Any thoughts would be much appreciated. Thanks for the help!
Save received data to some sort of permanent queue (MSMQ for example).
Exit handler.
Process data from queue in another application, for example in windows service.
This is not exactly "easy way", but safe and fast for customers.
Thanks everyone for your input. The way I went about this, so others can ponder it as a solution:
The queuing would probably be the most "correct" means, but it would take some extra implementation that really is just over the top for what I am intending to do. Using the information from http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx
First I create my processing class and spin that up in a background thread to do the work after I get the XML from the client. This releases the connection while my worker thread continues in the background.
I registered my processing class as an IRegisteredObject. I the implemented the Stop(bool immediate) method
public void Stop(bool immediate)
{
if (!immediate && _working)
return;//don't unregister yet, give it some time
if(immediate && _working)
{
//TODO: Log this instance
}
HostingEnvironment.UnregisterObject(this);
}
I set my _working variable to true when I am processing work, and unset it when done. If in the rare case I am processing work and stop gets called because the AppDomain is getting taken down, it will first just return without unregistering itself. This gives my process a bit more time to finish up. If when the method gets called the second time with the immediate flag set to true, it quickly logs the issue and then unregister itself.
This may not be the ultimate solution, but for my purposes, this will take care of alerting me when the very rare condition happens, as well as not holding up the client's connection as I process the data.
If you're using .NET 4.5, check out HttpTaskAsyncHandler.
From the linked page:
Asynchronous HTTP handlers The traditional approach to writing asynchronous handlers in ASP.NET is to implement the IHttpAsyncHandler
interface. ASP.NET 4.5 introduces the HttpTaskAsyncHandler
asynchronous base type that you can derive from, which makes it much
easier to write asynchronous handlers. The HttpTaskAsyncHandler type
is abstract and requires you to override the ProcessRequestAsync
method. Internally ASP.NET takes care of integrating the return
signature (a Task object) of ProcessRequestAsync with the older
asynchronous programming model used by the ASP.NET pipeline. The
following example shows how you can use Task and await as part of the
implementation of an asynchronous HTTP handler:
public class MyAsyncHandler : HttpTaskAsyncHandler
{
// ...
// ASP.NET automatically takes care of integrating the Task based override
// with the ASP.NET pipeline.
public override async Task ProcessRequestAsync(HttpContext context)
{
WebClient wc = new WebClient();
var result = await
wc.DownloadStringTaskAsync("http://www.microsoft.com");
// Do something with the result
}
}
Is it possible to return the page response to the user, before you've finished all your server side work?
Ie, I've got a cheap hosting account with No database, but I'd like to log a certain event, by calling a webservice on my other, more expensive hosting account (ie, a very slow logging operation)
I don't really want the user to have to wait for this slow logging operation to complete before their page is rendered.
Would I need to spin up a new thread, or make an asynchronous call? Or is it possible to return the page, and then continue working happily in the same thread/code?
Using ASP.Net (webforms) C# .Net 2.0 etc.
You would probably need a second thread. An easy option would be to use the ThreadPool, but in a more sophisticated setup a producer/consumer queue would work well.
At the simplest level:
ThreadPool.QueueUserWorkItem(delegate {
DoLogging(state details);
});
You sure can - try Response.Flush.
That being said - creating an asynchronous call may be the best way to do what you want to do. Response.Flush simply flushed the output buffer to the client, an asynchronous call would allow you to fire off a logging call and not have it impact the client's load time.
Keep in mind that an asynchronous call made during the page's life cycle in ASP.NET may not return in time for you to do anything with the response.