workflow-core icon indicating copy to clipboard operation
workflow-core copied to clipboard

How to terminate a running workflow?

Open SergiiKram opened this issue 3 years ago • 6 comments

The bug It appears that IWorkflowController.TerminateWorkflow(string workflowId) method does not work as one might expect. If the workflow is running then it returns false and does nothing.

That happens because the implementation first checks if there is a lock associated with the given workflow and if so it immediately returns false and does nothing. Since there is always a lock present for a running workflow, effectively it means that we can terminate only non-running workflows which is very unfortunate.

To Reproduce To reproduce the behavior:

  1. Start a new workflow that would run for a while.
  2. Try to Terminate or Suspend the workflow.
  3. Observe that the methods return false and do nothing.

Expected behavior I'd expect that methods succeed and workflow will be terminated once the current step execution is done.

Additional context Any workarounds on how to stop running workflow externally are welcome. Also, I'd be happy to implement the fix if you have a particular design for it, @danielgerlag.

There is a related issue https://github.com/danielgerlag/workflow-core/issues/751 too.

SergiiKram avatar Dec 30 '21 11:12 SergiiKram

Hello, i'm having the same problem. I'm looking for a workaround, but have found nothing yet

RMTTyszka avatar Jan 24 '22 14:01 RMTTyszka

Below is my workaround. Once step fails with retry, I will terminate it and persist workflow. Persistence is a must, otherwise it will be picked up and continue to execute.

var host = app.ApplicationServices.GetService<IWorkflowHost>();
host.OnStepError += (WorkflowInstance workflow, WorkflowStep step, Exception exception) =>
{
    try
    {
        host.TerminateWorkflow(workflow.Id);
        host.PersistenceStore.PersistWorkflow(workflow);
    }
    catch (Exception ex)
    {
        Logger?.Exception(LoggingEvents.WorkflowStepErrorHandling, ex);
    }
};

brucezlata avatar Jun 13 '22 03:06 brucezlata

My workaroun is to just release the lock before calling the workflowcontroller:

    await _lockProvider.ReleaseLock(workflowInstanceId);
    await _workflowController.TerminateWorkflow(workflowInstanceId);     

jjherscheid avatar Sep 04 '23 06:09 jjherscheid

We could look into creating some kind of scheduled termination, where the workflow will terminate at the end of the current execution cycle if it is locked. In the meantime, a good workaround would be to use a Library like Polly to create a retry policy until it is unlocked... I would strongly recommend NOT to hijack the lock from the executor as suggested by @jjherscheid , this could result in data corruption.

danielgerlag avatar Sep 04 '23 18:09 danielgerlag

Meanwhile, the approach mentioned here https://github.com/danielgerlag/workflow-core/pull/237 with Parallel flow and CancelCondition seems to work, so we can build a workaround to stop workflow from inside.

.Parallel()
    .Do(then => then
		.StartWith<DoSomething>()
		.WaitFor("Approval", (data, context) => context.Workflow.IdNow)
	)
    .Do(then => then
		.StartWith<DoSomething>()
		.Delay(data => TimeSpan.FromDays(3))
		.Then<EscalateIssue>()
	)
.Join()
	.CancelCondition(data => data.IsApproved, true)
	.Then<MoveAlong>();

SergiiKram avatar Dec 08 '23 15:12 SergiiKram

I used middleware to do the workaround, first create a step middleware and then check whether user has tried to suspend the workflow and waitforevent like 'Resume' if true. And at this point, the lock is releasted and workflow is suspended, you can now terminate or resume it at your will.

AngrySKL avatar Jan 03 '24 08:01 AngrySKL