Skip to main content

Interrupt a Workflow - Ruby SDK

This page shows how to interrupt a Workflow Execution.

You can interrupt a Workflow Execution in one of the following ways:

  • Cancel: Canceling a Workflow provides a graceful way to stop Workflow Execution.
  • Terminate: Terminating a Workflow forcefully stops Workflow Execution.

Terminating a Workflow forcefully stops Workflow Execution. This action resembles killing a process.

  • The system records a WorkflowExecutionTerminated event in the Workflow History.
  • The termination forcefully and immediately stops the Workflow Execution.
  • The Workflow code gets no chance to handle termination.
  • A Workflow Task doesn't get scheduled.

In most cases, canceling is preferable because it allows the Workflow to finish gracefully. Terminate only if the Workflow is stuck and cannot be canceled normally.

Cancellation

To give a Workflow and its Activities the ability to be cancelled, do the following:

  • Handle a Cancellation request within a Workflow.
  • Set Activity Heartbeat Timeouts.
  • Listen for and handle a Cancellation request within an Activity.
  • Send a Cancellation request from a Temporal Client.

Handle Cancellation in Workflow

Workflow Definitions can be written to respond to cancellation requests. It is common for an Activity to be run on Cancellation to perform cleanup.

Cancellation Requests on Workflows cancel the Temporalio::Workflow.cancellation which is a Temporalio::Cancellation that effectively serves as a cancellation token. This is the cancellation that is implicitly used for all calls within the workflow as well (e.g. Timers, Activities, etc) and therefore cancellation is propagated to them to be handled and bubble out.

class MyWorkflow < Temporalio::Workflow::Definition
def execute
# Whether this workflow waits on the activity to handle the cancellation or not is
# dependent upon the cancellation_type parameter. We leave the default here which
# sends the cancellation but does not wait on it to be handled.
Temporalio::Workflow.execute_activity(MyActivity, start_to_close_timeout: 100)
rescue Temporalio::Error => e
# For this sample, we only want to execute cleanup when it's a cancellation
raise unless Temporalio::Error.canceled?(e)

# Call a cleanup activity. We have to do this with a new/detached cancellation
# because the default workflow-level one is already canceled at this point.
Temporalio::Workflow.execute_activity(
MyCleanupActivity,
start_to_close_timeout: 100,
cancellation: Temporalio::Cancellation.new
)

# Re-raise the original exception
raise
end
end

Handle Cancellation in an Activity

Ensure that the Activity is Heartbeating to receive the Cancellation request and stop execution. Also make sure that the Heartbeat Timeout is set on the Activity Options when calling from the Workflow. An Activity Cancellation Request raises a Temporalio::Error::CanceledError in the Activity.

class MyActivity < Temporalio::Activity::Definition
def execute
# This is a naive loop simulating work, but similar heartbeat/cancellation logic
# applies to other scenarios as well
loop do
# Send heartbeat
Temporalio::Activity::Context.current.heartbeat
# Sleep before heartbeating again
sleep(3)
end
rescue Temporalio::Error::CanceledError
raise 'Canceled!'
end
end

Request Cancellation

Use cancel on the WorkflowHandle to cancel a Workflow Execution.

# Get a workflow handle by its workflow ID. This could be made specific to a run by
# passing run ID. This could also just be a handle that is returned from
# start_workflow instead.
handle = my_client.workflow_handle('my-workflow-id')

# Send cancellation. This returns when cancellation is received by the server. Wait on
# the handle's result to wait for cancellation to be applied.
handle.cancel

By default, Activities are automatically cancelled when the Workflow is cancelled since the workflow cancellation is used by activities by default. To issue a cancellation explicitly, a new cancellation token can be created.

class MyWorkflow < Temporalio::Workflow::Definition
def execute
# Create a new cancellation linked to the workflow one, so that it inherits
# cancellation that comes from the workflow. Users can choose to make it
# completely detached by not providing a parent.
cancellation, cancel_proc = Temporalio::Cancellation.new(
Temporalio::Workflow.cancellation
)

# Start the activity in the background. Whether this workflow waits on the activity
# to handle the cancellation or not is dependent upon the cancellation_type
# parameter. We leave the default here which sends the cancellation but does not wait
# on it to be handled.
future = Temporalio::Future.new do
Temporalio::Workflow.execute_activity(
MyActivity,
start_to_close_timeout: 100,
cancellation:
)
end

# Wait 5 minutes, then cancel it
Temporalio::Workflow.sleep(5 * 60)
cancel_proc.call

# Wait on the activity which will raise an activity error with a cause of
# cancellation which will fail the workflow
future.wait
end
end

Termination

To Terminate a Workflow Execution in Ruby, use the terminate method on the Workflow handle.

# Get a workflow handle by its workflow ID. This could be made specific to a run by
# passing run ID. This could also just be a handle that is returned from
# start_workflow instead.
handle = my_client.workflow_handle('my-workflow-id')

# Terminate
handle.terminate

Workflow Executions can also be Terminated directly from the WebUI. In this case, a custom note can be logged from the UI when that happens.