在長生命週期的流程中,有可能退單、有可能取消審核、外部系統可能逾時或條件失效,所以「取消」不是例外,是日常需求。
使用 Cancel 可以讓 Workflow 觸發補償邏輯、終止執行並留下完整的事件紀錄,確保流程在中途停止時,系統狀態仍保持一致與可追蹤。
CancellationScope
捕捉取消,做善後清理ActivityExecutionContext
或 Heartbeat 感知取消在 Workflow 內,要把「可能被取消」的區塊包在 CancellationScope
,並在捕捉到取消時進行清理。
清理動作則放在「不受取消影響」的區塊,用 Workflow.newDetachedCancellationScope(...)
。
import io.temporal.failure.CanceledFailure;
import io.temporal.workflow.CancellationScope;
import io.temporal.workflow.Workflow;
public class MyWorkflowImpl implements MyWorkflow {
private final MyActivities activities = Workflow.newActivityStub(
MyActivities.class,
ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofMinutes(5))
.build());
@Override
public void run(String input) {
// 先將要執行的操作放至可取消的範圍 CancellationScope
CancellationScope scope = Workflow.newCancellationScope(() -> {
activities.runningStep(input);
});
// ... 其他計算或流程
try {
// 開始執行,外部觸發 cancel 時,則拋出 CanceledFailure
scope.run();
} catch (CanceledFailure e) {
// 用 detached scope 做清理,避免清理也被取消打斷
Workflow.newDetachedCancellationScope(() -> {
activities.cleanup(input);
}).run();
// 視需求:可以選擇往上拋出,讓整個 Workflow 以 Canceled 結束
throw e;
}
}
}
從 Client 取消一個正在跑的 Workflow
public class ClientCancelExample {
public static void main(String[] args) {
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
WorkflowClient client = WorkflowClient.newInstance(service);
// 建立 typed stub
MyWorkflow myWorkflow = client.newWorkflowStub(
MyWorkflow.class,
WorkflowOptions.newBuilder()
.setTaskQueue("demo-queue")
.setWorkflowId("wf-cancel-demo")
.build());
// 啟動流程
WorkflowClient.start(myWorkflow::run, "payload");
// 發出取消請求
WorkflowStub workflowStub = WorkflowStub.fromTyped(myWorkflow);
workflowStub.cancel();
}
}
CancellationScope:把「可被取消」的一段邏輯包起來;當 client.cancel() 時,這段會收到取消訊號並拋出 CanceledFailure。
Workflow.newCancellationScope(() -> {
pay.charge(orderId);
Workflow.sleep(Duration.ofMinutes(30));
}).run();
如有「不想被取消打斷」的邏輯,可用 Workflow.newDetachedCancellationScope(() -> { ... }) 把它包起來,讓它不被上層取消波及(例如關鍵的補償步驟)。
Workflow.newDetachedCancellationScope(() -> {
refund(orderId); // 即使外部 cancel,這段也會完整執行
}).run();
WorkflowStub workflowStub = WorkflowStub.fromTyped(workflow);
workflowStub.cancel(); // 發出取消請求,Workflow 收到後觸發上述 catch
TRY_CANCEL
、WAIT_CANCELLATION_COMPLETED
、ABANDON
Cancel 是「優雅收尾」的機制:Client 下令、Workflow 接招、Activity/Child 配合,中間要靠 CancellationScope
、Heartbeat 與冪等清理來把風險控好。把「等待」設計成可取消,把「清理」設計成不會被取消打斷,你的流程就能進退自如。