Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# 5 .NET MVC long async task, progress report and cancel globally

I use ASP.Net MVC 5 and I have a long running action which have to poll webservices, process data and store them in database.

For that I want to use TPL library to start the task async.

But I wonder how to do 3 things :

  • I want to report progress of this task. For this I think about SignalR
  • I want to be able to left the page where I start this task from and be able to report the progression across the website (from a panel on the left but this is ok)
  • And I want to be able to cancel this task globally (from my panel on the left)

I know quite a few about all of technologies involved. But I'm not sure about the best way to achieve this.

Is someone can help me about the best solution ?

like image 864
Dragouf Avatar asked Oct 28 '13 16:10

Dragouf


2 Answers

The fact that you want to run long running work while the user can navigate away from the page that initiates the work means that you need to run this work "in the background". It cannot be performed as part of a regular HTTP request because the user might cancel his request at any time by navigating away or closing the browser. In fact this seems to be a key scenario for you.

Background work in ASP.NET is dangerous. You can certainly pull it off but it is not easy to get right. Also, worker processes can exit for many reasons (app pool recycle, deployment, machine reboot, machine failure, Stack Overflow or OOM exception on an unrelated thread). So make sure your long-running work tolerates being aborted mid-way. You can reduce the likelyhood that this happens but never exclude the possibility.

You can make your code safe in the face of arbitrary termination by wrapping all work in a transaction. This of course only works if you don't cause non-transacted side-effects like web-service calls that change state. It is not possible to give a general answer here because achieving safety in the presence of arbitrary termination depends highly on the concrete work to be done.

Here's a possible architecture that I have used in the past:

  • When a job comes in you write all necessary input data to a database table and report success to the client.
  • You need a way to start a worker to work on that job. You could start a task immediately for that. You also need a periodic check that looks for unstarted work in case the app exits after having added the work item but before starting a task for it. Have the Windows task scheduler call a secret URL in your app once per minute that does this.
  • When you start working on a job you mark that job as running so that it is not accidentally picked up a second time. Work on that job, write the results and mark it as done. All in a single transaction. When your process happens to exit mid-way the database will reset all data involved.
  • Write job progress to a separate table row on a separate connection and separate transaction. The browser can poll the server for progress information. You could also use SignalR but I don't have experience with that and I expect it would be hard to get it to resume progress reporting in the presence of arbitrary termination.
  • Cancellation would be done by setting a cancel flag in the progress information row. The app needs to poll that flag.

Maybe you can make use of message queueing for job processing but I'm always wary to use it. To process a message in a transacted way you need MSDTC which is unsupported with many high-availability solutions for SQL Server.

You might think that this architecture is not very sophisticated. It makes use of polling for lots of things. Polling is a primitive technique but it works quite well. It is reliable and well-understood. It has a simple concurrency model.

If you can assume that your application never exits at inopportune times the architecture would be much simpler. But this cannot be assumed. You cannot assume that there will be no deployments during work hours and that there will be no bugs leading to crashes.

like image 190
usr Avatar answered Sep 18 '22 20:09

usr


Even if using http worker is a bad thing to run long task I have made a small example of how to manage it with SignalR :

Inside this example you can :

  • Start a task
  • See task progression
  • Cancel task

It's based on :

  • twitter bootstrap
  • knockoutjs
  • signalR
  • C# 5.0 async/await with CancelToken and IProgress

You can find the source of this example here :

https://github.com/dragouf/SignalR.Progress

enter image description here

like image 26
Dragouf Avatar answered Sep 20 '22 20:09

Dragouf