Durable Task Framework定时器不触发问题求助
我在一项编排任务中,定时器基于编排输入对象指定的日期创建。该日期有时需要在编排启动后更新,我正尝试实现一个事件来取消现有定时器并创建带有更新日期的新定时器。目前已成功取消旧定时器并创建新定时器,但新定时器并未触发。在SQL数据库中可以看到新定时器已创建,但到了触发时间时编排似乎仍处于停滞状态。相关代码如下:
编排中的OnEvent方法
public override async void OnEvent(OrchestrationContext context, string eventName, string input) { IEventHandler eventHandler = await context.ScheduleTask<IEventHandler>(typeof(GetEventHandler), eventName); EventHandlerInput eventInput = new EventHandlerInput(context, customState, input); customState = eventHandler.HandleProcess(eventInput); }
日期变更事件处理器的HandleProcess方法
public CustomOrchestrationState HandleProcess<T>(T input) { EventHandlerInput customInput = (input as EventHandlerInput); if (!customInput.CustomOrchestrationState.AllowActionDateChange) { //date change not allowed throw new Exception(); } DateTime newDate = new DateTime(); try { newDate = Convert.ToDateTime(customInput.EventInput); } catch (Exception ex) { //Invalid Date format } CancellationTokenSource newToken = new CancellationTokenSource(); customInput.OrchestrationContext.CreateTimer(newDate, customInput, newToken.Token); customInput.CustomOrchestrationState.TimerCancelationToken?.Dispose(); CancelCurrentTimer(customInput.CustomOrchestrationState); customInput.CustomOrchestrationState.ActionDate = newDate; customInput.CustomOrchestrationState.TimerCancelationToken = newToken; return customInput.CustomOrchestrationState; }
请问这种行为是预期的还是Bug?我是否遗漏了某些步骤或操作有误?
这不是Bug,是代码中存在几个关键错误导致新定时器无法触发:
OnEvent方法使用async void
Durable Orchestration的事件处理方法必须返回async Task,而非async void。async void会导致运行时无法跟踪异步操作的完成状态,使得编排状态更新和新定时器的触发逻辑无法被正确处理。未等待
CreateTimer异步操作CreateTimer返回的Task必须被await,否则编排状态机不会记录该定时器的创建操作。即使数据库中能看到定时器记录,编排也不会响应触发事件。旧定时器取消逻辑错误
直接Dispose旧的CancellationTokenSource而未先调用Cancel(),可能无法正确取消旧定时器;同时取消和创建新定时器的顺序也不合理,应先取消旧定时器再创建新的。
修正后的代码
编排中的OnEvent方法
public override async Task OnEvent(OrchestrationContext context, string eventName, string input) { IEventHandler eventHandler = await context.ScheduleTask<IEventHandler>(typeof(GetEventHandler), eventName); EventHandlerInput eventInput = new EventHandlerInput(context, customState, input); // 等待异步的HandleProcess完成 customState = await eventHandler.HandleProcess(eventInput); }
日期变更事件处理器的HandleProcess方法
// 修改为异步方法,返回Task<CustomOrchestrationState> public async Task<CustomOrchestrationState> HandleProcess<T>(T input) { EventHandlerInput customInput = (input as EventHandlerInput); if (!customInput.CustomOrchestrationState.AllowActionDateChange) { throw new Exception("日期修改不被允许"); } DateTime newDate; try { newDate = Convert.ToDateTime(customInput.EventInput); } catch (Exception ex) { throw new Exception("日期格式无效", ex); } // 先取消并释放旧定时器的令牌源 var oldTokenSource = customInput.CustomOrchestrationState.TimerCancelationToken; if (oldTokenSource != null) { oldTokenSource.Cancel(); oldTokenSource.Dispose(); } // 创建新定时器并等待操作完成 CancellationTokenSource newToken = new CancellationTokenSource(); await customInput.OrchestrationContext.CreateTimer(newDate, customInput, newToken.Token); // 更新编排状态 customInput.CustomOrchestrationState.ActionDate = newDate; customInput.CustomOrchestrationState.TimerCancelationToken = newToken; return customInput.CustomOrchestrationState; }
关键说明
Durable Orchestration基于状态机运行,所有异步操作(包括CreateTimer、ScheduleTask等)必须被await,否则编排无法正确记录操作的完成状态,导致后续逻辑失效。同时,async void在Durable Functions中是严格禁止的,会引发不可预测的状态问题。
内容的提问来源于stack exchange,提问作者user27982908

