close
close
heart beat long running activity temporal typescript

heart beat long running activity temporal typescript

3 min read 23-11-2024
heart beat long running activity temporal typescript

This article explores how to build a robust system for monitoring long-running activities using Temporal, a powerful workflow orchestration engine, and TypeScript, a popular statically-typed language. We'll focus on implementing a "heartbeat" mechanism to ensure the health and proper execution of these activities. This pattern is crucial for maintaining the reliability of any system involving long-duration tasks.

Understanding the Challenge of Long-Running Activities

Long-running activities, those tasks that might take minutes, hours, or even days to complete, present unique challenges. Network interruptions, unexpected errors, or resource exhaustion can easily halt these activities. Without proper monitoring, detecting failures can be difficult, leading to data inconsistencies or complete task failures.

Introducing Temporal's Workflow Orchestration

Temporal excels at managing the complexities of long-running activities. Its core strength lies in its ability to orchestrate workflows, providing fault tolerance, retry mechanisms, and robust monitoring capabilities. This allows developers to focus on the core logic of the activity, leaving the orchestration and monitoring to Temporal.

The Heartbeat Pattern: Keeping Activities Alive

The heartbeat pattern involves periodically sending "heartbeat" signals from the long-running activity to the Temporal workflow. These signals confirm the activity's ongoing health. If the heartbeat stops, the workflow can detect the failure and initiate appropriate recovery actions (e.g., retry, alert, or escalation).

Implementing the Heartbeat with Temporal and TypeScript

Let's outline a basic implementation using Temporal's TypeScript SDK:

1. Defining the Activity

First, we define the long-running activity itself. This activity will include logic to periodically emit a heartbeat signal.

import { Activity } from "@temporalio/activity";

@Activity({
  name: "MyLongRunningActivity",
})
export class MyLongRunningActivity {
  async execute(input: any): Promise<any> {
    // ... Long-running task logic ...
    const heartbeatInterval = 10000; // Send heartbeat every 10 seconds

    try {
      while (true) {
        // ... Perform some work ...
        await this.heartbeat(input); //Emit Heartbeat
        await new Promise((resolve) => setTimeout(resolve, heartbeatInterval));
      }
    } catch (error) {
      // Handle errors gracefully
      console.error("Error in long-running activity:", error);
      throw error; 
    }
  }

  async heartbeat(input:any){
    //Send Heartbeat through Temporal. This requires a custom heartbeat handler 
    // within the workflow (see next section).  Implementation details depend on your 
    // specific Temporal setup and might involve custom signals, queries, etc.
    await this.emitHeartbeat(input); //Example function, implement based on your setup
  }

  //Example function, implement based on your setup and chosen heartbeat method
  async emitHeartbeat(input:any){
    console.log("Heartbeat sent!", input);
  }
}

2. Defining the Workflow

The workflow will initiate the long-running activity and monitor its heartbeat. If the heartbeat stops after a certain timeout, the workflow will take appropriate action.

import { Workflow, WorkflowQuery } from "@temporalio/workflow";
import { MyLongRunningActivity } from "./my-activity";


@Workflow({
  name: "MyLongRunningWorkflow",
})
export class MyLongRunningWorkflow {
  @WorkflowQuery()
  async getWorkflowStatus(): Promise<string> {
    return this.status;
  }
  
  async run(input: any): Promise<any> {
    this.status = "Running";
    const activityStub = this.activityStub(MyLongRunningActivity);
    const heartbeatTimeout = 60000; // 60-second timeout
    let lastHeartbeat = Date.now();
    
    try{
      const result = await activityStub.execute(input);
      this.status = "Completed";
      return result;
    } catch(e){
      if(Date.now() - lastHeartbeat > heartbeatTimeout) {
        this.status = "Heartbeat Timeout! Activity failed."
        console.error("Heartbeat timeout: Activity failed.");
        // Handle failure (e.g., retry, alert)
      } else {
        this.status = "Activity Failed. Check logs for details"
        console.error("Activity failed:", e);
        //Handle activity failure
      }
      throw e;
    }
    
    
  }

  private status: string;

}

Important: The emitHeartbeat and the corresponding heartbeat handling in the workflow are placeholders. The specific implementation will depend on how you choose to implement heartbeat communication within the Temporal framework (e.g., using signals, queries, or custom extensions).

3. Error Handling and Retries

Robust error handling is paramount. Wrap activity code in try...catch blocks. Implement retry logic using Temporal's built-in retry mechanisms for transient errors.

4. Monitoring and Alerting

Integrate with a monitoring system to track activity health and alert on failures. Temporal's built-in visibility features can provide valuable insights.

Conclusion

Implementing the heartbeat pattern with Temporal and TypeScript enables reliable monitoring of long-running activities. This allows for early detection of failures, improved resilience, and more robust overall system behavior. Remember to carefully consider your heartbeat frequency, timeout settings, and error handling strategies based on your application's specific requirements. This pattern is crucial for any application heavily reliant on lengthy operations.

Related Posts