Blazor Server run only one background task - c#

I have a Blazor Server application (net 7) where a long running background task can be triggered by any user, but once the task has been started everyone must wait until that task is completed.
The users visit a Razor page then leave the page and maybe log-off, but the background task should keep running.
So I need some kind of static background task which updates the UI (via IProgress or something) when someone visits the page and also is only allowed to be triggered once. Sounds like a classic singleton Task, but is that right here?
I know of IHostedService but I don't need a queue of Jobs to be consumed infinitely, only one single task static task running in background and showing each user who visits a Razor page what the current status of the task is.
[Update 1] Just for clarification, it is not a very long running task, still the behavior should be as described.

Related

Long-running task without IHostedService running the entire life of the application?

I have a website page that needs the option of performing an operation that could take several minutes. To avoid performance issues and time outs, I want to run this operation outside of the HTTP request.
After some research, I found IHostedService and BackgroundService, which can be registered as a singleton using AddHostedService<T>().
But my concern is that a hosted service is always running. Doesn't that seem like a waste of resources when I just want it to run on demand?
Does anyone know a better option to run a lengthy task, or a way to use IHostedService that doesn't need to run endlessly?
Note that the operation calls and waits for an API call. And so I cannot report the progress of the operation, nor can I set a flag in a common database regarding whether the operation has completed.
One option to run a lengthy task on demand while avoiding performance issues and time outs is to use a message queue. You can have your Razor Pages website send a message to the queue when the operation is requested, and have a separate service, such as a background worker, consume messages from the queue and perform the operation. This allows you to decouple the task from the web request, and also allows for the possibility of adding more worker instances to handle the workload.
Another option is to use a task scheduler that runs on demand, such as Hangfire. It allows you to schedule background jobs and monitor their progress, which can be useful in your scenario where you cannot report the progress of the operation.
You can also use IHostedService, but you need to make sure that the service is only running when it is needed. You can use a flag or a semaphore to control whether the service is running or not. You can set the flag or semaphore when the operation is requested, and clear it when the operation is completed. The service can then check the flag or semaphore in its main loop, and exit if the flag is not set.
In summary:
message queue, task scheduler, and IHostedService with controlling flag/semaphore are all viable options for running a lengthy task on demand. The best option depends on your specific use case and requirements.

HostingEnvironment.QueueBackgroundWorkItem - Clarification?

I've read Stephen's article about fire and forget background actions in Asp.net.
It is not recommended to use Task.Run for fire-and-forget because Asp.net doesn't know that you've queued a task.
So if a recycle is about to occur, the task has no way of knowing it.
That's where HostingEnvironment.QueueBackgroundWorkItem gets in.
It will know that a recycle is about to happen and will invoke the Cancellation Token.
But!
FWIK - Background tasks are being "terminated" once the main thread has finished.
That means that if a request gets in (a new thread is being created/fetched) and it invokesTask.Run , and the response has finished (but Task has not) then the Task will be terminated.
Question:
Does QueueBackgroundWorkItem solve this problem ? or does it only exist to warn about recycle?
In other words, if there's a request which runs QueueBackgroundWorkItem and the response has finished, will the QueueBackgroundWorkItem continue to execute its code?
The docs say: " independent of any request", but I'm not sure if it answers my question though
According to the documentation, this method tries to delay application shutdown until background work has completed.
Differs from a normal ThreadPool work item in that ASP.NET can keep track of how many work items registered through this API are currently running, and the ASP.NET runtime will try to delay AppDomain shutdown until these work items have finished executing.
Also, it does not flow certain contexts which are associated with the current request and are inappropriate for request-independent background work:
This overloaded method doesn't flow the ExecutionContext or SecurityContext from the caller to the callee. Therefore, members of those objects, such as the CurrentPrincipal property, will not flow from the caller to the callee.
In ASP.NET there is no way to make sure that background work ever completes. The machine could blue screen, there could be a bug terminating the worker process, there could be a timeout forcing termination and many other things.
Or, your code could have a bug and crash. That causes the queued work to be lost as well.
If you need something executed reliably, execute it synchronously before confirming completion, or queue it somewhere (message queue, database, ...).
That means that if a request gets in (a new thread is being created/fetched) and it invokesTask.Run, and the response has finished (but Task has not) then the Task will be terminated.
No, Task.Run works independently of HTTP requests. In fact, there is no way to cancel a Task except if the task's code cancels itself.

Service vs Thread

What should I use to make an application that will:
Ask the user for username and password
Authorize
Run an infinite loop in which it will fetch some data from the website every 10 seconds or so.
I want to be able to do some basic tasks in the meantime, or lock my screen without the thread getting killed. I don't want the service to continue running after I close the application, I just want to be sure the thread is never killed while it's running for a long time.
I also wanted to ask: Are services as easy to interact with as threads? Can I just pass a CancellationToken in it and cancel it when the user presses the stop button?
I also found the setThreadPriority, will it help in my case?
Services and Threads are totally different concepts. A Thread is a separate process that executes in parallel. A Service is a component of an app that doesn't have a UI and runs with a separate life cycle. A service does not run on its own thread, it runs on the UI thread (although it can launch a Thread if it wishes).
You use a Service if you want to do some task but not be bound to the Android Activity lifecycle. You use a Thread if you want to run in parallel. If you want both, then you use a Service that launches a Thread.
From what I'm reading (you don't want the Thread to continue after the Activity is finished), you want a Thread and not a Service.
A service can run in isolation (while your app is not necessarily running). A thread can be spun off from either your app itself, or a service.

HostingEnvironment.QueueBackgroundWorkItem() in ASP.NET for small background tasks

I came a across a nice little tool that has been added to ASP.NET in v4.5.2
I am wandering how safe it is and how one can effectively utilize it in an ASP.NET MVC or Web API scenario.
I know I am always wanting to do a quick and simple fire and forget task in my web applications. For example:
Sending an emails/s
Sending push notifications
Logging analytics or errors to the db
Now typically I just create a method called
public async Task SendEmailAsync(string to, string body)
{
//TODO: send email
}
and I would use it like so:
public async Task<ActionResult> Index()
{
...
await SendEmailAsync(User.Identity.Username, "Hello");
return View();
}
now my concern with this is that, I am delaying the user in order to send my email to them. This doesn't make much sense to me.
So I first considered just doing:
Task.Run(()=> SendEmailAsync(User.Identity.Username, "Hello"));
however when reading up about this. It is apparently not the best thing to do in IIS environment. (i'm not 100% sure on the specifics).
So this is where I came across HostingEnvironment.QueueBackgroundWorkItem(x=> SendEmailAsync(User.Identity.Username, "Hello"));
This is a very quick and easy way to offload the send email task to a background worker and serve up the users View() much quicker.
Now I am aware this is not for tasks running longer than 90 seconds and is not 100% guaranteed executution.
But my question is:
Is HostingEnvironment.QueueBackgroundWorkItem() sufficient for: sending emails, push notifications, db queries etc in a standard ASP.NET web site.
It depends.
The main benefit of QueueBackgroundWorkItem is the following, emphasis mine (source):
Differs from a normal ThreadPool work item in that ASP.NET can keep track of how many work items registered through this API are currently running, and the ASP.NET runtime will try to delay AppDomain shutdown until these work items have finished executing.
Essentially, QueueBackgroundWorkItem helps you run tasks that might take a couple of seconds by attempting not to shutdown your application while there's still a task running.
Running a normal database query or sending out a push notification should be a matter of a couple hundred milliseconds (or a few seconds); neither should take a very long time and should thus be fine to run within QueueBackgroundWorkItem.
However, there's no guarantee for the task to finish — as you said, the task is not awaited. It all depends on the importance of the task to execute. If the task must complete, it's not a good candidate for QueueBackgroundWorkItem.

Code Task Scheduler Suggestions

I'm trying to determine if something exists that allows you to run code in a distributed way. Each code task will have an Id and allow for smart scheduling.
Does not allow code with the same ID to run at the same time. Task will wait in a queue before being executed.
Task can trigger other tasks to be run.
If a multiple tasks trigger the same task (determined by id) to run, only one task will be triggered and not two.
It shouldn't be too difficult to write your own task scheduler and deploy it as a Windows service. This way you can ensure the scheduling rules you have outlined are followed.
Otherwise, you can have a look at Quartz.NET and see if it meets your needs.

Categories