User Guide
Features
Cancellation

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.

Cancellation Mechanism in Node.js

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.

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.

Checking for Cancellation

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:

  1. 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.

  2. 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.

  3. 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.

  4. Error Handling: Handle cancellation errors appropriately. Distinguish between cancellation errors and other types of errors to provide meaningful error messages and take appropriate actions.

  5. 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.