async Task <OrchestrationInstance> InternalCreateOrchestrationInstanceWithRaisedEventAsync( string orchestrationName, string orchestrationVersion, string orchestrationInstanceId, object orchestrationInput, IDictionary <string, string> orchestrationTags, OrchestrationStatus[] dedupeStatuses, string eventName, object eventData, DateTime?startAt = null) { TraceContextBase requestTraceContext = null; // correlation CorrelationTraceClient.Propagate(() => { requestTraceContext = this.CreateOrExtractRequestTraceContext(eventName); }); if (string.IsNullOrWhiteSpace(orchestrationInstanceId)) { orchestrationInstanceId = Guid.NewGuid().ToString("N"); } var orchestrationInstance = new OrchestrationInstance { InstanceId = orchestrationInstanceId, ExecutionId = Guid.NewGuid().ToString("N"), }; string serializedOrchestrationData = this.defaultConverter.Serialize(orchestrationInput); var startedEvent = new ExecutionStartedEvent(-1, serializedOrchestrationData) { Tags = orchestrationTags, Name = orchestrationName, Version = orchestrationVersion, OrchestrationInstance = orchestrationInstance, ScheduledStartTime = startAt }; var startMessage = new TaskMessage { OrchestrationInstance = orchestrationInstance, Event = startedEvent }; this.logHelper.SchedulingOrchestration(startedEvent); CorrelationTraceClient.Propagate(() => this.CreateAndTrackDependencyTelemetry(requestTraceContext)); // Raised events and create orchestration calls use different methods so get handled separately await this.ServiceClient.CreateTaskOrchestrationAsync(startMessage, dedupeStatuses); if (eventData != null) { string serializedEventData = this.defaultConverter.Serialize(eventData); var eventRaisedEvent = new EventRaisedEvent(-1, serializedEventData) { Name = eventName }; this.logHelper.RaisingEvent(orchestrationInstance, eventRaisedEvent); var eventMessage = new TaskMessage { OrchestrationInstance = new OrchestrationInstance { InstanceId = orchestrationInstanceId, // to ensure that the event gets raised on the running // orchestration instance, null the execution id // so that it will find out which execution // it should use for processing ExecutionId = null }, Event = eventRaisedEvent, }; await this.ServiceClient.SendTaskOrchestrationMessageAsync(eventMessage); } return(orchestrationInstance); }
async Task OnProcessWorkItemAsync(TaskActivityWorkItem workItem) { Task renewTask = null; var renewCancellationTokenSource = new CancellationTokenSource(); try { TaskMessage taskMessage = workItem.TaskMessage; OrchestrationInstance orchestrationInstance = taskMessage.OrchestrationInstance; if (string.IsNullOrWhiteSpace(orchestrationInstance?.InstanceId)) { throw TraceHelper.TraceException( TraceEventType.Error, "TaskActivityDispatcher-MissingOrchestrationInstance", new InvalidOperationException("Message does not contain any OrchestrationInstance information")); } if (taskMessage.Event.EventType != EventType.TaskScheduled) { throw TraceHelper.TraceException( TraceEventType.Critical, "TaskActivityDispatcher-UnsupportedEventType", new NotSupportedException("Activity worker does not support event of type: " + taskMessage.Event.EventType)); } // call and get return message var scheduledEvent = (TaskScheduledEvent)taskMessage.Event; TaskActivity taskActivity = this.objectManager.GetObject(scheduledEvent.Name, scheduledEvent.Version); if (taskActivity == null) { throw new TypeMissingException($"TaskActivity {scheduledEvent.Name} version {scheduledEvent.Version} was not found"); } renewTask = Task.Factory.StartNew(() => RenewUntil(workItem, renewCancellationTokenSource.Token), renewCancellationTokenSource.Token); // TODO : pass workflow instance data var context = new TaskContext(taskMessage.OrchestrationInstance); HistoryEvent eventToRespond = null; var dispatchContext = new DispatchMiddlewareContext(); dispatchContext.SetProperty(taskMessage.OrchestrationInstance); dispatchContext.SetProperty(taskActivity); dispatchContext.SetProperty(scheduledEvent); await this.dispatchPipeline.RunAsync(dispatchContext, async _ => { try { string output = await taskActivity.RunAsync(context, scheduledEvent.Input); eventToRespond = new TaskCompletedEvent(-1, scheduledEvent.EventId, output); } catch (TaskFailureException e) { TraceHelper.TraceExceptionInstance(TraceEventType.Error, "TaskActivityDispatcher-ProcessTaskFailure", taskMessage.OrchestrationInstance, e); string details = IncludeDetails ? e.Details : null; eventToRespond = new TaskFailedEvent(-1, scheduledEvent.EventId, e.Message, details); } catch (Exception e) when(!Utils.IsFatal(e)) { TraceHelper.TraceExceptionInstance(TraceEventType.Error, "TaskActivityDispatcher-ProcessException", taskMessage.OrchestrationInstance, e); string details = IncludeDetails ? $"Unhandled exception while executing task: {e}\n\t{e.StackTrace}" : null; eventToRespond = new TaskFailedEvent(-1, scheduledEvent.EventId, e.Message, details); } }); var responseTaskMessage = new TaskMessage { Event = eventToRespond, OrchestrationInstance = orchestrationInstance }; await this.orchestrationService.CompleteTaskActivityWorkItemAsync(workItem, responseTaskMessage); } finally { if (renewTask != null) { renewCancellationTokenSource.Cancel(); try { await renewTask; } catch (OperationCanceledException) { // ignore } } } }
async Task OnProcessWorkItemAsync(TaskActivityWorkItem workItem) { Task renewTask = null; var renewCancellationTokenSource = new CancellationTokenSource(); TaskMessage taskMessage = workItem.TaskMessage; OrchestrationInstance orchestrationInstance = taskMessage.OrchestrationInstance; TaskScheduledEvent scheduledEvent = null; try { if (string.IsNullOrWhiteSpace(orchestrationInstance?.InstanceId)) { this.logHelper.TaskActivityDispatcherError( workItem, $"The activity worker received a message that does not have any OrchestrationInstance information."); throw TraceHelper.TraceException( TraceEventType.Error, "TaskActivityDispatcher-MissingOrchestrationInstance", new InvalidOperationException("Message does not contain any OrchestrationInstance information")); } if (taskMessage.Event.EventType != EventType.TaskScheduled) { this.logHelper.TaskActivityDispatcherError( workItem, $"The activity worker received an event of type '{taskMessage.Event.EventType}' but only '{EventType.TaskScheduled}' is supported."); throw TraceHelper.TraceException( TraceEventType.Critical, "TaskActivityDispatcher-UnsupportedEventType", new NotSupportedException("Activity worker does not support event of type: " + taskMessage.Event.EventType)); } // call and get return message scheduledEvent = (TaskScheduledEvent)taskMessage.Event; this.logHelper.TaskActivityStarting(orchestrationInstance, scheduledEvent); TaskActivity taskActivity = this.objectManager.GetObject(scheduledEvent.Name, scheduledEvent.Version); if (taskActivity == null) { throw new TypeMissingException($"TaskActivity {scheduledEvent.Name} version {scheduledEvent.Version} was not found"); } if (workItem.LockedUntilUtc < DateTime.MaxValue) { // start a task to run RenewUntil renewTask = Task.Factory.StartNew( () => this.RenewUntil(workItem, renewCancellationTokenSource.Token), renewCancellationTokenSource.Token); } // TODO : pass workflow instance data var context = new TaskContext(taskMessage.OrchestrationInstance); HistoryEvent eventToRespond = null; var dispatchContext = new DispatchMiddlewareContext(); dispatchContext.SetProperty(taskMessage.OrchestrationInstance); dispatchContext.SetProperty(taskActivity); dispatchContext.SetProperty(scheduledEvent); await this.dispatchPipeline.RunAsync(dispatchContext, async _ => { // correlation CorrelationTraceClient.Propagate(() => workItem.TraceContextBase?.SetActivityToCurrent()); try { string output = await taskActivity.RunAsync(context, scheduledEvent.Input); eventToRespond = new TaskCompletedEvent(-1, scheduledEvent.EventId, output); } catch (TaskFailureException e) { TraceHelper.TraceExceptionInstance(TraceEventType.Error, "TaskActivityDispatcher-ProcessTaskFailure", taskMessage.OrchestrationInstance, e); string details = this.IncludeDetails ? e.Details : null; eventToRespond = new TaskFailedEvent(-1, scheduledEvent.EventId, e.Message, details); this.logHelper.TaskActivityFailure(orchestrationInstance, scheduledEvent.Name, (TaskFailedEvent)eventToRespond, e); CorrelationTraceClient.Propagate(() => CorrelationTraceClient.TrackException(e)); } catch (Exception e) when(!Utils.IsFatal(e) && !Utils.IsExecutionAborting(e)) { TraceHelper.TraceExceptionInstance(TraceEventType.Error, "TaskActivityDispatcher-ProcessException", taskMessage.OrchestrationInstance, e); string details = this.IncludeDetails ? $"Unhandled exception while executing task: {e}\n\t{e.StackTrace}" : null; eventToRespond = new TaskFailedEvent(-1, scheduledEvent.EventId, e.Message, details); this.logHelper.TaskActivityFailure(orchestrationInstance, scheduledEvent.Name, (TaskFailedEvent)eventToRespond, e); } if (eventToRespond is TaskCompletedEvent completedEvent) { this.logHelper.TaskActivityCompleted(orchestrationInstance, scheduledEvent.Name, completedEvent); } }); var responseTaskMessage = new TaskMessage { Event = eventToRespond, OrchestrationInstance = orchestrationInstance }; await this.orchestrationService.CompleteTaskActivityWorkItemAsync(workItem, responseTaskMessage); } catch (SessionAbortedException e) { // The activity aborted its execution this.logHelper.TaskActivityAborted(orchestrationInstance, scheduledEvent, e.Message); TraceHelper.TraceInstance(TraceEventType.Warning, "TaskActivityDispatcher-ExecutionAborted", orchestrationInstance, "{0}: {1}", scheduledEvent.Name, e.Message); await this.orchestrationService.AbandonTaskActivityWorkItemAsync(workItem); } finally { if (renewTask != null) { renewCancellationTokenSource.Cancel(); try { // wait the renewTask finish await renewTask; } catch (OperationCanceledException) { // ignore } } } }
static TaskMessage ProcessWorkflowCompletedTaskDecision( OrchestrationCompleteOrchestratorAction completeOrchestratorAction, OrchestrationRuntimeState runtimeState, bool includeDetails, out bool continuedAsNew) { ExecutionCompletedEvent executionCompletedEvent; continuedAsNew = (completeOrchestratorAction.OrchestrationStatus == OrchestrationStatus.ContinuedAsNew); if (completeOrchestratorAction.OrchestrationStatus == OrchestrationStatus.ContinuedAsNew) { executionCompletedEvent = new ContinueAsNewEvent(completeOrchestratorAction.Id, completeOrchestratorAction.Result); } else { executionCompletedEvent = new ExecutionCompletedEvent(completeOrchestratorAction.Id, completeOrchestratorAction.Result, completeOrchestratorAction.OrchestrationStatus); } runtimeState.AddEvent(executionCompletedEvent); TraceHelper.TraceInstance( runtimeState.OrchestrationStatus == OrchestrationStatus.Failed ? TraceEventType.Warning : TraceEventType.Information, "TaskOrchestrationDispatcher-InstanceCompleted", runtimeState.OrchestrationInstance, "Instance Id '{0}' completed in state {1} with result: {2}", runtimeState.OrchestrationInstance, runtimeState.OrchestrationStatus, completeOrchestratorAction.Result); TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-InstanceCompletionEvents", runtimeState.OrchestrationInstance, () => Utils.EscapeJson(DataConverter.Serialize(runtimeState.Events, true))); // Check to see if we need to start a new execution if (completeOrchestratorAction.OrchestrationStatus == OrchestrationStatus.ContinuedAsNew) { var taskMessage = new TaskMessage(); var startedEvent = new ExecutionStartedEvent(-1, completeOrchestratorAction.Result) { OrchestrationInstance = new OrchestrationInstance { InstanceId = runtimeState.OrchestrationInstance.InstanceId, ExecutionId = Guid.NewGuid().ToString("N") }, Tags = runtimeState.Tags, ParentInstance = runtimeState.ParentInstance, Name = runtimeState.Name, Version = completeOrchestratorAction.NewVersion ?? runtimeState.Version }; taskMessage.OrchestrationInstance = startedEvent.OrchestrationInstance; taskMessage.Event = startedEvent; return(taskMessage); } // If this is a Sub Orchestration than notify the parent by sending a complete message if (runtimeState.ParentInstance != null) { var taskMessage = new TaskMessage(); if (completeOrchestratorAction.OrchestrationStatus == OrchestrationStatus.Completed) { var subOrchestrationCompletedEvent = new SubOrchestrationInstanceCompletedEvent(-1, runtimeState.ParentInstance.TaskScheduleId, completeOrchestratorAction.Result); taskMessage.Event = subOrchestrationCompletedEvent; } else if (completeOrchestratorAction.OrchestrationStatus == OrchestrationStatus.Failed || completeOrchestratorAction.OrchestrationStatus == OrchestrationStatus.Terminated) { var subOrchestrationFailedEvent = new SubOrchestrationInstanceFailedEvent(-1, runtimeState.ParentInstance.TaskScheduleId, completeOrchestratorAction.Result, includeDetails ? completeOrchestratorAction.Details : null); taskMessage.Event = subOrchestrationFailedEvent; } if (taskMessage.Event != null) { taskMessage.OrchestrationInstance = runtimeState.ParentInstance.OrchestrationInstance; return(taskMessage); } } return(null); }
/// <summary> /// Method to process a new work item /// </summary> /// <param name="workItem">The work item to process</param> protected async Task OnProcessWorkItemAsync(TaskOrchestrationWorkItem workItem) { var messagesToSend = new List <TaskMessage>(); var timerMessages = new List <TaskMessage>(); var subOrchestrationMessages = new List <TaskMessage>(); bool isCompleted = false; bool continuedAsNew = false; ExecutionStartedEvent continueAsNewExecutionStarted = null; TaskMessage continuedAsNewMessage = null; OrchestrationRuntimeState runtimeState = workItem.OrchestrationRuntimeState; runtimeState.AddEvent(new OrchestratorStartedEvent(-1)); if (!ReconcileMessagesWithState(workItem)) { // TODO : mark an orchestration as faulted if there is data corruption TraceHelper.TraceSession( TraceEventType.Error, "TaskOrchestrationDispatcher-DeletedOrchestration", runtimeState.OrchestrationInstance?.InstanceId, "Received result for a deleted orchestration"); isCompleted = true; } else { TraceHelper.TraceInstance( TraceEventType.Verbose, "TaskOrchestrationDispatcher-ExecuteUserOrchestration-Begin", runtimeState.OrchestrationInstance, "Executing user orchestration: {0}", DataConverter.Serialize(runtimeState.GetOrchestrationRuntimeStateDump(), true)); IList <OrchestratorAction> decisions = (await ExecuteOrchestrationAsync(runtimeState)).ToList(); TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-ExecuteUserOrchestration-End", runtimeState.OrchestrationInstance, "Executed user orchestration. Received {0} orchestrator actions: {1}", decisions.Count(), string.Join(", ", decisions.Select(d => d.Id + ":" + d.OrchestratorActionType))); foreach (OrchestratorAction decision in decisions) { TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-ProcessOrchestratorAction", runtimeState.OrchestrationInstance, "Processing orchestrator action of type {0}", decision.OrchestratorActionType); switch (decision.OrchestratorActionType) { case OrchestratorActionType.ScheduleOrchestrator: messagesToSend.Add( ProcessScheduleTaskDecision((ScheduleTaskOrchestratorAction)decision, runtimeState, IncludeParameters)); break; case OrchestratorActionType.CreateTimer: var timerOrchestratorAction = (CreateTimerOrchestratorAction)decision; timerMessages.Add(ProcessCreateTimerDecision(timerOrchestratorAction, runtimeState)); break; case OrchestratorActionType.CreateSubOrchestration: var createSubOrchestrationAction = (CreateSubOrchestrationAction)decision; subOrchestrationMessages.Add( ProcessCreateSubOrchestrationInstanceDecision(createSubOrchestrationAction, runtimeState, IncludeParameters)); break; case OrchestratorActionType.OrchestrationComplete: TaskMessage workflowInstanceCompletedMessage = ProcessWorkflowCompletedTaskDecision((OrchestrationCompleteOrchestratorAction)decision, runtimeState, IncludeDetails, out continuedAsNew); if (workflowInstanceCompletedMessage != null) { // Send complete message to parent workflow or to itself to start a new execution // Store the event so we can rebuild the state if (continuedAsNew) { continuedAsNewMessage = workflowInstanceCompletedMessage; continueAsNewExecutionStarted = workflowInstanceCompletedMessage.Event as ExecutionStartedEvent; } else { subOrchestrationMessages.Add(workflowInstanceCompletedMessage); } } isCompleted = !continuedAsNew; break; default: throw TraceHelper.TraceExceptionInstance( TraceEventType.Error, "TaskOrchestrationDispatcher-UnsupportedDecisionType", runtimeState.OrchestrationInstance, new NotSupportedException("decision type not supported")); } // Underlying orchestration service provider may have a limit of messages per call, to avoid the situation // we keep on asking the provider if message count is ok and stop processing new decisions if not. // // We also put in a fake timer to force next orchestration task for remaining messages int totalMessages = messagesToSend.Count + subOrchestrationMessages.Count + timerMessages.Count; if (this.orchestrationService.IsMaxMessageCountExceeded(totalMessages, runtimeState)) { TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-MaxMessageCountReached", runtimeState.OrchestrationInstance, "MaxMessageCount reached. Adding timer to process remaining events in next attempt."); if (isCompleted || continuedAsNew) { TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-OrchestrationAlreadyCompleted", runtimeState.OrchestrationInstance, "Orchestration already completed. Skip adding timer for splitting messages."); break; } var dummyTimer = new CreateTimerOrchestratorAction { Id = FrameworkConstants.FakeTimerIdToSplitDecision, FireAt = DateTime.UtcNow }; timerMessages.Add(ProcessCreateTimerDecision(dummyTimer, runtimeState)); break; } } } // finish up processing of the work item if (!continuedAsNew) { runtimeState.AddEvent(new OrchestratorCompletedEvent(-1)); } OrchestrationRuntimeState newOrchestrationRuntimeState = workItem.OrchestrationRuntimeState; OrchestrationState instanceState = null; if (isCompleted) { TraceHelper.TraceSession(TraceEventType.Information, "TaskOrchestrationDispatcher-DeletingSessionState", workItem.InstanceId, "Deleting session state"); if (newOrchestrationRuntimeState.ExecutionStartedEvent != null) { instanceState = Utils.BuildOrchestrationState(newOrchestrationRuntimeState); } newOrchestrationRuntimeState = null; } else { instanceState = Utils.BuildOrchestrationState(newOrchestrationRuntimeState); if (continuedAsNew) { TraceHelper.TraceSession( TraceEventType.Information, "TaskOrchestrationDispatcher-UpdatingStateForContinuation", workItem.InstanceId, "Updating state for continuation"); newOrchestrationRuntimeState = new OrchestrationRuntimeState(); newOrchestrationRuntimeState.AddEvent(new OrchestratorStartedEvent(-1)); newOrchestrationRuntimeState.AddEvent(continueAsNewExecutionStarted); newOrchestrationRuntimeState.AddEvent(new OrchestratorCompletedEvent(-1)); } } await this.orchestrationService.CompleteTaskOrchestrationWorkItemAsync( workItem, newOrchestrationRuntimeState, continuedAsNew?null : messagesToSend, subOrchestrationMessages, continuedAsNew?null : timerMessages, continuedAsNewMessage, instanceState); }
/// <summary> /// Method to process a new work item /// </summary> /// <param name="workItem">The work item to process</param> protected async Task <bool> OnProcessWorkItemAsync(TaskOrchestrationWorkItem workItem) { var messagesToSend = new List <TaskMessage>(); var timerMessages = new List <TaskMessage>(); var orchestratorMessages = new List <TaskMessage>(); var isCompleted = false; var continuedAsNew = false; var isInterrupted = false; // correlation CorrelationTraceClient.Propagate(() => CorrelationTraceContext.Current = workItem.TraceContext); ExecutionStartedEvent continueAsNewExecutionStarted = null; TaskMessage continuedAsNewMessage = null; IList <HistoryEvent> carryOverEvents = null; string carryOverStatus = null; OrchestrationRuntimeState runtimeState = workItem.OrchestrationRuntimeState; runtimeState.AddEvent(new OrchestratorStartedEvent(-1)); OrchestrationRuntimeState originalOrchestrationRuntimeState = runtimeState; OrchestrationState instanceState = null; // Assumes that: if the batch contains a new "ExecutionStarted" event, it is the first message in the batch. if (!this.ReconcileMessagesWithState(workItem)) { // TODO : mark an orchestration as faulted if there is data corruption this.logHelper.DroppingOrchestrationWorkItem(workItem, "Received result for a deleted orchestration"); TraceHelper.TraceSession( TraceEventType.Error, "TaskOrchestrationDispatcher-DeletedOrchestration", runtimeState.OrchestrationInstance?.InstanceId, "Received result for a deleted orchestration"); isCompleted = true; } else { do { continuedAsNew = false; continuedAsNewMessage = null; this.logHelper.OrchestrationExecuting(runtimeState.OrchestrationInstance, runtimeState.Name); TraceHelper.TraceInstance( TraceEventType.Verbose, "TaskOrchestrationDispatcher-ExecuteUserOrchestration-Begin", runtimeState.OrchestrationInstance, "Executing user orchestration: {0}", DataConverter.Serialize(runtimeState.GetOrchestrationRuntimeStateDump(), true)); if (workItem.Cursor == null) { workItem.Cursor = await this.ExecuteOrchestrationAsync(runtimeState, workItem); } else { await this.ResumeOrchestrationAsync(workItem); } IReadOnlyList <OrchestratorAction> decisions = workItem.Cursor.LatestDecisions.ToList(); this.logHelper.OrchestrationExecuted( runtimeState.OrchestrationInstance, runtimeState.Name, decisions); TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-ExecuteUserOrchestration-End", runtimeState.OrchestrationInstance, "Executed user orchestration. Received {0} orchestrator actions: {1}", decisions.Count, string.Join(", ", decisions.Select(d => d.Id + ":" + d.OrchestratorActionType))); foreach (OrchestratorAction decision in decisions) { TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-ProcessOrchestratorAction", runtimeState.OrchestrationInstance, "Processing orchestrator action of type {0}", decision.OrchestratorActionType); switch (decision.OrchestratorActionType) { case OrchestratorActionType.ScheduleOrchestrator: var scheduleTaskAction = (ScheduleTaskOrchestratorAction)decision; var message = this.ProcessScheduleTaskDecision( scheduleTaskAction, runtimeState, this.IncludeParameters); messagesToSend.Add(message); break; case OrchestratorActionType.CreateTimer: var timerOrchestratorAction = (CreateTimerOrchestratorAction)decision; timerMessages.Add(this.ProcessCreateTimerDecision( timerOrchestratorAction, runtimeState, isInternal: false)); break; case OrchestratorActionType.CreateSubOrchestration: var createSubOrchestrationAction = (CreateSubOrchestrationAction)decision; orchestratorMessages.Add( this.ProcessCreateSubOrchestrationInstanceDecision( createSubOrchestrationAction, runtimeState, this.IncludeParameters)); break; case OrchestratorActionType.SendEvent: var sendEventAction = (SendEventOrchestratorAction)decision; orchestratorMessages.Add( this.ProcessSendEventDecision(sendEventAction, runtimeState)); break; case OrchestratorActionType.OrchestrationComplete: OrchestrationCompleteOrchestratorAction completeDecision = (OrchestrationCompleteOrchestratorAction)decision; TaskMessage workflowInstanceCompletedMessage = this.ProcessWorkflowCompletedTaskDecision(completeDecision, runtimeState, this.IncludeDetails, out continuedAsNew); if (workflowInstanceCompletedMessage != null) { // Send complete message to parent workflow or to itself to start a new execution // Store the event so we can rebuild the state carryOverEvents = null; if (continuedAsNew) { continuedAsNewMessage = workflowInstanceCompletedMessage; continueAsNewExecutionStarted = workflowInstanceCompletedMessage.Event as ExecutionStartedEvent; if (completeDecision.CarryoverEvents.Any()) { carryOverEvents = completeDecision.CarryoverEvents.ToList(); completeDecision.CarryoverEvents.Clear(); } } else { orchestratorMessages.Add(workflowInstanceCompletedMessage); } } isCompleted = !continuedAsNew; break; default: throw TraceHelper.TraceExceptionInstance( TraceEventType.Error, "TaskOrchestrationDispatcher-UnsupportedDecisionType", runtimeState.OrchestrationInstance, new NotSupportedException($"Decision type '{decision.OrchestratorActionType}' not supported")); } // Underlying orchestration service provider may have a limit of messages per call, to avoid the situation // we keep on asking the provider if message count is ok and stop processing new decisions if not. // // We also put in a fake timer to force next orchestration task for remaining messages int totalMessages = messagesToSend.Count + orchestratorMessages.Count + timerMessages.Count; if (this.orchestrationService.IsMaxMessageCountExceeded(totalMessages, runtimeState)) { TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-MaxMessageCountReached", runtimeState.OrchestrationInstance, "MaxMessageCount reached. Adding timer to process remaining events in next attempt."); if (isCompleted || continuedAsNew) { TraceHelper.TraceInstance( TraceEventType.Information, "TaskOrchestrationDispatcher-OrchestrationAlreadyCompleted", runtimeState.OrchestrationInstance, "Orchestration already completed. Skip adding timer for splitting messages."); break; } var dummyTimer = new CreateTimerOrchestratorAction { Id = FrameworkConstants.FakeTimerIdToSplitDecision, FireAt = DateTime.UtcNow }; timerMessages.Add(this.ProcessCreateTimerDecision( dummyTimer, runtimeState, isInternal: true)); isInterrupted = true; break; } } // correlation CorrelationTraceClient.Propagate(() => { if (runtimeState.ExecutionStartedEvent != null) { runtimeState.ExecutionStartedEvent.Correlation = CorrelationTraceContext.Current.SerializableTraceContext; } }); // finish up processing of the work item if (!continuedAsNew && runtimeState.Events.Last().EventType != EventType.OrchestratorCompleted) { runtimeState.AddEvent(new OrchestratorCompletedEvent(-1)); } if (isCompleted) { TraceHelper.TraceSession(TraceEventType.Information, "TaskOrchestrationDispatcher-DeletingSessionState", workItem.InstanceId, "Deleting session state"); if (runtimeState.ExecutionStartedEvent != null) { instanceState = Utils.BuildOrchestrationState(runtimeState); } } else { if (continuedAsNew) { TraceHelper.TraceSession( TraceEventType.Information, "TaskOrchestrationDispatcher-UpdatingStateForContinuation", workItem.InstanceId, "Updating state for continuation"); // correlation CorrelationTraceClient.Propagate(() => { continueAsNewExecutionStarted.Correlation = CorrelationTraceContext.Current.SerializableTraceContext; }); runtimeState = new OrchestrationRuntimeState(); runtimeState.AddEvent(new OrchestratorStartedEvent(-1)); runtimeState.AddEvent(continueAsNewExecutionStarted); runtimeState.Status = workItem.OrchestrationRuntimeState.Status ?? carryOverStatus; carryOverStatus = workItem.OrchestrationRuntimeState.Status; if (carryOverEvents != null) { foreach (var historyEvent in carryOverEvents) { runtimeState.AddEvent(historyEvent); } } runtimeState.AddEvent(new OrchestratorCompletedEvent(-1)); workItem.OrchestrationRuntimeState = runtimeState; TaskOrchestration newOrchestration = this.objectManager.GetObject( runtimeState.Name, continueAsNewExecutionStarted.Version); workItem.Cursor = new OrchestrationExecutionCursor( runtimeState, newOrchestration, new TaskOrchestrationExecutor( runtimeState, newOrchestration, this.orchestrationService.EventBehaviourForContinueAsNew), latestDecisions: null); if (workItem.LockedUntilUtc < DateTime.UtcNow.AddMinutes(1)) { await this.orchestrationService.RenewTaskOrchestrationWorkItemLockAsync(workItem); } } instanceState = Utils.BuildOrchestrationState(runtimeState); } } while (continuedAsNew); } workItem.OrchestrationRuntimeState = originalOrchestrationRuntimeState; runtimeState.Status = runtimeState.Status ?? carryOverStatus; if (instanceState != null) { instanceState.Status = runtimeState.Status; } await this.orchestrationService.CompleteTaskOrchestrationWorkItemAsync( workItem, runtimeState, continuedAsNew?null : messagesToSend, orchestratorMessages, continuedAsNew?null : timerMessages, continuedAsNewMessage, instanceState); workItem.OrchestrationRuntimeState = runtimeState; return(isCompleted || continuedAsNew || isInterrupted); }