Hangfire: Cancelling a processing job does not kill its thread

Created on 5 Jun 2016  路  13Comments  路  Source: HangfireIO/Hangfire

When deleting a job it didn't kill the worker thread even passing CancelationToken as parameter.

How to stop a long running thread?

question

All 13 comments

Do you call the ThrowIfCancellationRequested method inside your method?

@odinserj Following examples I didn't find any solution of cancelling a task without a loop.

I did a task (I call as plugin) which executes SSIS package (ETL) and lasts at least for 3h. Sometimes I need to simply stop its processing. How can I call ThrowIfCancellationRequested when the method is waiting for the response? It's like the same as Thread.Sleep().

Piece of code

``` C#
protected override void Execute()
{
var eventListener = new SqlPackageEventListener();
eventListener.OnPackageVerbose += OnPackageVerbose;

var app = new Application();
var pkg = app.LoadPackage(PackageLocation, eventListener);

//Below is the synchronous execution of SSIS package and lasts the necessary time of each package   
var pkgResults = pkg.Execute(null, null, eventListener, null, null);

if (pkgResults != DTSExecResult.Success)
    throw new Exception(eventListener.Error);

Log.Info("The package executed successfully.");

}
```

How to cancel this method, keeping the safety in mind?

reading few docs on stackoverflow on how to make a sync method to act as async I could realize that's "simple" and possible.

So I'll start over again and try to make that job be async. My initial questions are:

  1. The entry method of my plugin should have IJobCancellationToken as parameter?
  2. Where should I call ThrowIfCancellationRequested before or after the Execute?

``` C#
//Entry method
public void Run(IJobCancellationToken ct)
{
Log.Info("Starting execution of {0}", Name);
try
{
//ct.ThrowIfCancellationRequested();
Execute();
}
catch (OperationCanceledException)
{
Log.Error("Cancellation requested, exiting...");
throw;
}
catch (Exception ex)
{
Log.Error(ex);
throw;
}
finally
{
Log.Info("Finishing execution of {0}", Name);
}
}

protected override void Execute()
{
var eventListener = new SqlPackageEventListener();
eventListener.OnPackageVerbose += OnPackageVerbose;

var app = new Application();
var pkg = app.LoadPackage(PackageLocation, eventListener);
var pkgResults = pkg.Execute(null, null, eventListener, null, null);

if (pkgResults != DTSExecResult.Success)
    throw new Exception(eventListener.Error);

Log.Info("The package executed successfully.");

}
```

image

Is there pkg.ExecuteAsync method that takes a CancellationToken instance? Without this the only way to stop the method execution is to abort the whole thread. Thread.Abort method call is very dangerous.

Unfortunately there's no async method implemented. I'm using Microsoft.SqlServer.Dts.Runtime based on 2008 R2 (https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.dts.runtime(v=sql.105).aspx)

Do you see another way of doing it?
I know that Thread.Abort is not a good idea at all.

If we forget about Hangfire, how would you cancel the execution of this method?

Simply aborting the thread. Even though it's not safe (as said) but in my production environment sometimes is mandatory.

The question is : How can I call to abort the thread when deleting a processing job?

You can start a new thread in your method, and call the pkg.Execute method in that thread. After starting a thread, you can wait for the thread completion together with polling the IJobCancellationToken instance (with delays to not to stress the storage). If it throws, then abort the thread and rethrow the exception.

// Pseudocode, just to show the intention
var thread = new Thread(Execute);
thread.Start();

while (!thread.Join(TimeSpan.FromSeconds(1)))
{
    try
    {
        token.ThrowIfCancellationRequested();
    }
    catch (OperationCanceledException)
    {
        thread.Abort();
        throw;
    }
}

I don't like to teach Hangfire how to abort threads, because it is not a correct way of doing things. In your case, Thread.Abort is fine, in other cases it is not.

It is very strange that DTS API doesn't take any CancellationToken instances to be able to cancel a query.

I'm not sure what you're doing in the package, but can the package be broken up into (much) smaller parts and run it batches ? That way you can check the cancellation token between batches.

@yangman The package actually is a SSIS package (ETL) that executes various sql instructions. There's no way at this time of breaking up in smaller ones.

There's a IDTSEvents.OnQueryCancel event, which is raised periodically to determine if package execution should be cancelled.

You should subscribe to that event and return cancellationToken.IsCancellationRequested value.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cottsak picture cottsak  路  3Comments

dbones picture dbones  路  3Comments

nsnail picture nsnail  路  3Comments

cbmek picture cbmek  路  3Comments

osmanrahimi picture osmanrahimi  路  3Comments