Cancellation in Hatchet Workflows
Hatchet provides a mechanism for canceling workflow executions gracefully, allowing you to stop running workflows and their associated steps when needed. Cancellation can be triggered on graceful termination of a worker or automatically through concurrency control strategies like CANCEL_IN_PROGRESS
, which cancels currently running workflow instances to free up slots for new instances when the concurrency limit is reached.
When a workflow is canceled, Hatchet sends a cancellation signal to all the currently executing steps. The steps can then check for the cancellation signal and take appropriate action, such as cleaning up resources, aborting network requests, or gracefully terminating their execution.
Cancellation Mechanisms
Hatchet uses the standard AbortController
and AbortSignal
interfaces from Node.js to handle cancellation. Each step in a workflow has access to a context.controller
property, which is an instance of AbortController
. The AbortController
provides a way to signal cancellation to the step and any asynchronous operations it may be performing.
Inside a step function, you can check for cancellation by accessing the signal
property of the context.controller
. The signal
property is an instance of AbortSignal
, which provides a way to register event listeners and check the cancellation status.
Here's an example of how to check for cancellation in a step:
const myStep: Step = async (context: Context) => {
const { signal } = context.controller;
if (signal.aborted) {
// The step has been canceled
// Perform any necessary cleanup or termination logic
return;
}
// Rest of the step logic
// ...
// You can also register an event listener for cancellation
signal.addEventListener("abort", () => {
// Cancellation signal received
// Perform any necessary cleanup or termination logic
});
// Perform asynchronous operations
// ...
};
In this example, the step first checks the aborted
property of the signal
to determine if the step has been canceled. If aborted
is true
, the step can perform any necessary cleanup or termination logic and return early.
Additionally, you can register an event listener for the 'abort'
event on the signal
. This listener will be called when the cancellation signal is received, allowing you to handle the cancellation asynchronously.
Aborting Network Requests
One common use case for cancellation is aborting network requests that are no longer needed. Hatchet's cancellation mechanism integrates seamlessly with many network libraries that support the AbortSignal
interface.
Here's an example of how to pass the AbortSignal
to an axios
request:
import axios from "axios";
const myStep: Step = async (context: Context) => {
const { signal } = context.controller;
try {
const response = await axios.get("https://api.example.com/data", {
signal,
});
// Handle the response
// ...
} catch (error) {
if (axios.isCancel(error)) {
// Request was canceled
console.log("Request canceled");
} else {
// Handle other errors
// ...
}
}
};
In this example, the signal
property from the context.controller
is passed as an option to the axios.get
request. If the step is canceled while the request is still pending, the request will be automatically aborted, and an error will be thrown. You can catch the error and check if it was due to cancellation using the axios.isCancel
method.
Cancellation Best Practices
When working with cancellation in Hatchet workflows, consider the following best practices:
-
Graceful Termination: When a step receives a cancellation signal, aim to terminate its execution gracefully. Clean up any resources, abort pending operations, and perform any necessary cleanup tasks before returning from the step function.
-
Cancellation Checks: Regularly check for cancellation signals within long-running steps or loops. This allows the step to respond to cancellation in a timely manner and avoid unnecessary processing.
-
Asynchronous Operations: If a step performs asynchronous operations, such as network requests or file I/O, consider passing the cancellation signal to those operations. Many libraries and APIs support cancellation through the
AbortSignal
interface. -
Error Handling: Handle cancellation errors appropriately. Distinguish between cancellation errors and other types of errors to provide meaningful error messages and take appropriate actions.
-
Cancellation Propagation: If a step invokes other functions or libraries, consider propagating the cancellation signal to those dependencies. This ensures that cancellation is handled consistently throughout the workflow.
Conclusion
Cancellation is a powerful feature in Hatchet that allows you to gracefully stop workflow executions when needed. Remember to follow best practices when implementing cancellation in your workflows, such as graceful termination, regular cancellation checks, handling asynchronous operations, proper error handling, and cancellation propagation.
By incorporating cancellation into your Hatchet workflows, you can build more resilient and responsive systems that can adapt to changing circumstances and user needs.