public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception) { _eventPublisher.PublishNotification(new WorkflowError { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version, ExecutionPointerId = pointer.Id, StepId = step.Id, Message = exception.Message }); pointer.Status = PointerStatus.Failed; var queue = new Queue <ExecutionPointer>(); queue.Enqueue(pointer); while (queue.Count > 0) { var exceptionPointer = queue.Dequeue(); var exceptionStep = def.Steps.FindById(exceptionPointer.StepId); var shouldCompensate = ShouldCompensate(workflow, def, exceptionPointer); var errorOption = (exceptionStep.ErrorBehavior ?? (shouldCompensate ? WorkflowErrorHandling.Compensate : def.DefaultErrorBehavior)); foreach (var handler in _errorHandlers.Where(x => x.Type == errorOption)) { handler.Handle(workflow, def, exceptionPointer, exceptionStep, exception, queue); } } }
private bool ShouldCompensate(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer currentPointer) { var scope = new Stack <string>(currentPointer.Scope); scope.Push(currentPointer.Id); while (scope.Count > 0) { var pointerId = scope.Pop(); var pointer = workflow.ExecutionPointers.FindById(pointerId); var step = def.Steps.FindById(pointer.StepId); if ((step.CompensationStepId.HasValue) || (step.RevertChildrenAfterCompensation)) { return(true); } } return(false); }
private bool InitializeStep(WorkflowInstance workflow, WorkflowStep step, WorkflowExecutorResult wfResult, WorkflowDefinition def, ExecutionPointer pointer) { switch (step.InitForExecution(wfResult, def, workflow, pointer)) { case ExecutionPipelineDirective.Defer: return(false); case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.UtcNow; return(false); } if (pointer.Status != PointerStatus.Running) { pointer.Status = PointerStatus.Running; _publisher.PublishNotification(new StepStarted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, ExecutionPointerId = pointer.Id, StepId = step.Id, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); } if (!pointer.StartTime.HasValue) { pointer.StartTime = _datetimeProvider.UtcNow; } return(true); }
private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowExecutorResult wfResult, WorkflowDefinition def) { using (var scope = _scopeProvider.CreateScope()) { _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); IStepBody body = step.ConstructBody(scope.ServiceProvider); if (body == null) { _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = $"Unable to construct step body {step.BodyType.ToString()}" }); return; } IStepExecutionContext context = new StepExecutionContext() { Workflow = workflow, Step = step, PersistenceData = pointer.PersistenceData, ExecutionPointer = pointer, Item = pointer.ContextItem }; foreach (var input in step.Inputs) { input.AssignInput(workflow.Data, body, context); } switch (step.BeforeExecute(wfResult, context, pointer, body)) { case ExecutionPipelineDirective.Defer: return; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); await _workflowController.PublishEvent("WorkFlowEnded", workflow.Id, null); return; } var result = await body.RunAsync(context); if (result.Proceed) { foreach (var output in step.Outputs) { output.AssignOutput(workflow.Data, body, context); } } _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); step.AfterExecute(wfResult, context, result, pointer); } }
private int?FindScopeCompensationStepId(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer currentPointer) { var scope = new Stack <string>(currentPointer.Scope); scope.Push(currentPointer.Id); while (scope.Count > 0) { var pointerId = scope.Pop(); var pointer = workflow.ExecutionPointers.First(x => x.Id == pointerId); var step = def.Steps.First(x => x.Id == pointer.StepId); if (step.CompensationStepId.HasValue) { return(step.CompensationStepId.Value); } } return(null); }
public void HandleStepException(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step) { pointer.Status = PointerStatus.Failed; var compensatingStepId = FindScopeCompensationStepId(workflow, def, pointer); var errorOption = (step.ErrorBehavior ?? (compensatingStepId.HasValue ? WorkflowErrorHandling.Compensate : def.DefaultErrorBehavior)); SelectErrorStrategy(errorOption, workflow, def, pointer, step); }
public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, Exception exception, Queue <ExecutionPointer> bubbleUpQueue) { pointer.RetryCount++; pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(step.RetryInterval ?? def.DefaultErrorRetryInterval ?? _options.ErrorRetryInterval); step.PrimeForRetry(pointer); }
public override ExecutionPipelineDirective BeforeExecute(WorkflowExecutorResult executorResult, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { if (executionPointer.EventPublished) { if ((body is UserStep) && (executionPointer.EventData is UserAction)) { (body as UserStep).UserAction = (executionPointer.EventData as UserAction); executionPointer.ExtensionAttributes["ActionUser"] = (executionPointer.EventData as UserAction).User; } } return(ExecutionPipelineDirective.Next); }
public override ExecutionPipelineDirective BeforeExecute(WorkflowExecutorResult executorResult, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { if (executionPointer.EventPublished) { if (body is ISubscriptionBody) { (body as ISubscriptionBody).EventData = executionPointer.EventData; } } return(ExecutionPipelineDirective.Next); }
public override void AfterWorkflowIteration(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { base.AfterWorkflowIteration(executorResult, defintion, workflow, executionPointer); var taskStep = workflow.ExecutionPointers.Find(x => x.Id == executionPointer.PredecessorId); if (taskStep.EventPublished) { executionPointer.EndTime = DateTime.Now.ToUniversalTime(); executionPointer.Active = false; } }
public override ExecutionPipelineDirective InitForExecution(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { if (!executionPointer.EventPublished) { if (EventKey != null) { executionPointer.EventKey = Convert.ToString(EventKey.Compile().DynamicInvoke(workflow.Data)); } DateTime effectiveDate = DateTime.MinValue; if (EffectiveDate != null) { effectiveDate = Convert.ToDateTime(EffectiveDate.Compile().DynamicInvoke(workflow.Data)); } effectiveDate = effectiveDate.ToUniversalTime(); executionPointer.EventName = EventName; executionPointer.Active = false; executorResult.Subscriptions.Add(new EventSubscription() { WorkflowId = workflow.Id, StepId = executionPointer.StepId, EventName = executionPointer.EventName, EventKey = executionPointer.EventKey, SubscribeAsOf = effectiveDate }); return(ExecutionPipelineDirective.Defer); } return(ExecutionPipelineDirective.Next); }
public override ExecutionPipelineDirective InitForExecution(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { return(ExecutionPipelineDirective.EndWorkflow); }
public ExecutionPointer BuildCompensationPointer(WorkflowDefinition def, ExecutionPointer pointer, ExecutionPointer exceptionPointer, int compensationStepId) { var nextId = GenerateId(); return(new ExecutionPointer() { Id = nextId, PredecessorId = exceptionPointer.Id, StepId = compensationStepId, Active = true, ContextItem = pointer.ContextItem, Status = PointerStatus.Pending, StepName = def.Steps.First(x => x.Id == compensationStepId).Name, Scope = new Stack <string>(pointer.Scope) }); }
public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, ExecutionResult result, WorkflowExecutorResult workflowResult) { pointer.PersistenceData = result.PersistenceData; pointer.Outcome = result.OutcomeValue; if (result.SleepFor.HasValue) { pointer.SleepUntil = _datetimeProvider.UtcNow.Add(result.SleepFor.Value); pointer.Status = PointerStatus.Sleeping; } if (!string.IsNullOrEmpty(result.EventName)) { pointer.EventName = result.EventName; pointer.EventKey = result.EventKey; pointer.Active = false; pointer.Status = PointerStatus.WaitingForEvent; workflowResult.Subscriptions.Add(new EventSubscription { WorkflowId = workflow.Id, StepId = pointer.StepId, ExecutionPointerId = pointer.Id, EventName = pointer.EventName, EventKey = pointer.EventKey, SubscribeAsOf = result.EventAsOf, SubscriptionData = result.SubscriptionData }); } if (result.Proceed) { pointer.Active = false; pointer.EndTime = _datetimeProvider.UtcNow; pointer.Status = PointerStatus.Complete; foreach (var outcomeTarget in step.Outcomes.Where(x => x.Matches(result, workflow.Data))) { workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, pointer, outcomeTarget)); } var pendingSubsequents = workflow.ExecutionPointers .FindByStatus(PointerStatus.PendingPredecessor) .Where(x => x.PredecessorId == pointer.Id); foreach (var subsequent in pendingSubsequents) { subsequent.Status = PointerStatus.Pending; subsequent.Active = true; } _eventPublisher.PublishNotification(new StepCompleted { EventTimeUtc = _datetimeProvider.UtcNow, Reference = workflow.Reference, ExecutionPointerId = pointer.Id, StepId = step.Id, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); } else { foreach (var branch in result.BranchValues) { foreach (var childDefId in step.Children) { workflow.ExecutionPointers.Add(_pointerFactory.BuildChildPointer(def, pointer, childDefId, branch)); } } } }
public override ExecutionPipelineDirective InitForExecution(IWorkflowHost host, IPersistenceProvider persistenceStore, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { if (!executionPointer.EventPublished) { //resolve principal to be assigned var resolvedUser = Principal.Compile().DynamicInvoke(workflow.Data); executionPointer.ExtensionAttributes["AssignedPrincipal"] = resolvedUser; executionPointer.ExtensionAttributes["Prompt"] = UserPrompt; Dictionary <string, object> userOptions = new Dictionary <string, object>(); foreach (var outcome in Outcomes) { userOptions[outcome.Label ?? Convert.ToString(outcome.Value ?? "Proceed")] = outcome.Value; } executionPointer.ExtensionAttributes["UserOptions"] = userOptions; executionPointer.EventKey = workflow.Id + "." + executionPointer.Id; executionPointer.EventName = "UserAction"; executionPointer.Active = false; persistenceStore.PersistWorkflow(workflow).Wait(); host.SubscribeEvent(workflow.Id, executionPointer.StepId, executionPointer.EventName, executionPointer.EventKey); return(ExecutionPipelineDirective.Defer); } return(ExecutionPipelineDirective.Next); }
public override ExecutionPipelineDirective InitForExecution(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { if (!executionPointer.EventPublished) { //resolve principal to be assigned var resolvedUser = Principal.Compile().DynamicInvoke(workflow.Data); executionPointer.ExtensionAttributes["AssignedPrincipal"] = resolvedUser; executionPointer.ExtensionAttributes["Prompt"] = UserPrompt; Dictionary <string, object> userOptions = new Dictionary <string, object>(); foreach (var outcome in Outcomes) { userOptions[outcome.Label ?? Convert.ToString(outcome.GetValue(workflow.Data) ?? "Proceed")] = outcome.GetValue(workflow.Data); } executionPointer.ExtensionAttributes["UserOptions"] = userOptions; executionPointer.EventKey = workflow.Id + "." + executionPointer.Id; executionPointer.EventName = "UserAction"; executionPointer.Active = false; executorResult.Subscriptions.Add(new EventSubscription() { WorkflowId = workflow.Id, StepId = executionPointer.StepId, EventName = executionPointer.EventName, EventKey = executionPointer.EventKey, SubscribeAsOf = DateTime.Now.ToUniversalTime() }); return(ExecutionPipelineDirective.Defer); } return(ExecutionPipelineDirective.Next); }
public override ExecutionPipelineDirective BeforeExecute(IWorkflowHost host, IPersistenceProvider persistenceStore, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { if (executionPointer.EventPublished) { if ((body is UserStepBody) && (executionPointer.EventData is UserAction)) { (body as UserStepBody).UserAction = (executionPointer.EventData as UserAction); executionPointer.ExtensionAttributes["ActionUser"] = (executionPointer.EventData as UserAction).User; } } return(ExecutionPipelineDirective.Next); }
private void Compensate(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer exceptionPointer) { var scope = new Stack <string>(exceptionPointer.Scope); scope.Push(exceptionPointer.Id); exceptionPointer.Active = false; exceptionPointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); exceptionPointer.Status = PointerStatus.Failed; while (scope.Any()) { var pointerId = scope.Pop(); var pointer = workflow.ExecutionPointers.First(x => x.Id == pointerId); var step = def.Steps.First(x => x.Id == pointer.StepId); var resume = true; var revert = false; if (scope.Any()) { var parentId = scope.Peek(); var parentPointer = workflow.ExecutionPointers.First(x => x.Id == parentId); var parentStep = def.Steps.First(x => x.Id == parentPointer.StepId); resume = parentStep.ResumeChildrenAfterCompensation; revert = parentStep.RevertChildrenAfterCompensation; } if ((step.ErrorBehavior ?? WorkflowErrorHandling.Compensate) != WorkflowErrorHandling.Compensate) { SelectErrorStrategy(step.ErrorBehavior ?? WorkflowErrorHandling.Retry, workflow, def, pointer, step); continue; } if (step.CompensationStepId.HasValue) { pointer.Active = false; pointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); pointer.Status = PointerStatus.Compensated; var compensationPointer = _pointerFactory.BuildCompensationPointer(def, pointer, exceptionPointer, step.CompensationStepId.Value); workflow.ExecutionPointers.Add(compensationPointer); if (resume) { foreach (var outcomeTarget in step.Outcomes.Where(x => x.GetValue(workflow.Data) == null)) { workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, pointer, outcomeTarget)); } } } if (revert) { var prevSiblings = workflow.ExecutionPointers .Where(x => pointer.Scope.SequenceEqual(x.Scope) && x.Id != pointer.Id && x.Status == PointerStatus.Complete) .OrderByDescending(x => x.EndTime) .ToList(); foreach (var siblingPointer in prevSiblings) { var siblingStep = def.Steps.First(x => x.Id == siblingPointer.StepId); if (siblingStep.CompensationStepId.HasValue) { var compensationPointer = _pointerFactory.BuildCompensationPointer(def, siblingPointer, exceptionPointer, siblingStep.CompensationStepId.Value); workflow.ExecutionPointers.Add(compensationPointer); siblingPointer.Status = PointerStatus.Compensated; } } } } }
private void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, ExecutionResult result, WorkflowExecutorResult workflowResult) { //TODO: refactor this into it's own class pointer.PersistenceData = result.PersistenceData; pointer.Outcome = result.OutcomeValue; if (result.SleepFor.HasValue) { pointer.SleepUntil = DateTime.Now.ToUniversalTime().Add(result.SleepFor.Value); } if (!string.IsNullOrEmpty(result.EventName)) { pointer.EventName = result.EventName; pointer.EventKey = result.EventKey; pointer.Active = false; workflowResult.Subscriptions.Add(new EventSubscription() { WorkflowId = workflow.Id, StepId = pointer.StepId, EventName = pointer.EventName, EventKey = pointer.EventKey, SubscribeAsOf = result.EventAsOf }); } if (result.Proceed) { pointer.Active = false; pointer.EndTime = DateTime.Now.ToUniversalTime(); foreach (var outcomeTarget in step.Outcomes.Where(x => object.Equals(x.GetValue(workflow.Data), result.OutcomeValue) || x.GetValue(workflow.Data) == null)) { workflow.ExecutionPointers.Add(new ExecutionPointer() { Id = Guid.NewGuid().ToString(), PredecessorId = pointer.Id, StepId = outcomeTarget.NextStep, Active = true, ContextItem = pointer.ContextItem, StepName = def.Steps.First(x => x.Id == outcomeTarget.NextStep).Name }); } } else { foreach (var branch in result.BranchValues) { foreach (var childDefId in step.Children) { var childPointerId = Guid.NewGuid().ToString(); workflow.ExecutionPointers.Add(new ExecutionPointer() { Id = childPointerId, PredecessorId = pointer.Id, StepId = childDefId, Active = true, ContextItem = branch, StepName = def.Steps.First(x => x.Id == childDefId).Name }); pointer.Children.Add(childPointerId); } } } }
public void ProcessExecutionResult(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step, ExecutionResult result, WorkflowExecutorResult workflowResult) { pointer.PersistenceData = result.PersistenceData; pointer.Outcome = result.OutcomeValue; if (result.SleepFor.HasValue) { pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(result.SleepFor.Value); pointer.Status = PointerStatus.Sleeping; } if (!string.IsNullOrEmpty(result.EventName)) { pointer.EventName = result.EventName; pointer.EventKey = result.EventKey; pointer.Active = false; pointer.Status = PointerStatus.WaitingForEvent; workflowResult.Subscriptions.Add(new EventSubscription() { WorkflowId = workflow.Id, StepId = pointer.StepId, EventName = pointer.EventName, EventKey = pointer.EventKey, SubscribeAsOf = result.EventAsOf }); } if (result.Proceed) { pointer.Active = false; pointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); pointer.Status = PointerStatus.Complete; foreach (var outcomeTarget in step.Outcomes.Where(x => object.Equals(x.GetValue(workflow.Data), result.OutcomeValue) || x.GetValue(workflow.Data) == null)) { workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, pointer, outcomeTarget)); } } else { foreach (var branch in result.BranchValues) { foreach (var childDefId in step.Children) { workflow.ExecutionPointers.Add(_pointerFactory.BuildChildPointer(def, pointer, childDefId, branch)); } } } }
internal static WorkflowInstance ToWorkflowInstance(this PersistedWorkflow instance) { WorkflowInstance result = new WorkflowInstance(); result.Data = JsonConvert.DeserializeObject(instance.Data, SerializerSettings); result.Description = instance.Description; result.Reference = instance.Reference; result.Id = instance.InstanceId.ToString(); result.NextExecution = instance.NextExecution; result.Version = instance.Version; result.WorkflowDefinitionId = instance.WorkflowDefinitionId; result.Status = instance.Status; result.CreateTime = DateTime.SpecifyKind(instance.CreateTime, DateTimeKind.Utc); if (instance.CompleteTime.HasValue) { result.CompleteTime = DateTime.SpecifyKind(instance.CompleteTime.Value, DateTimeKind.Utc); } result.ExecutionPointers = new ExecutionPointerCollection(instance.ExecutionPointers.Count + 8); foreach (var ep in instance.ExecutionPointers) { var pointer = new ExecutionPointer(); pointer.Id = ep.Id; pointer.StepId = ep.StepId; pointer.Active = ep.Active; if (ep.SleepUntil.HasValue) { pointer.SleepUntil = DateTime.SpecifyKind(ep.SleepUntil.Value, DateTimeKind.Utc); } pointer.PersistenceData = JsonConvert.DeserializeObject(ep.PersistenceData ?? string.Empty, SerializerSettings); if (ep.StartTime.HasValue) { pointer.StartTime = DateTime.SpecifyKind(ep.StartTime.Value, DateTimeKind.Utc); } if (ep.EndTime.HasValue) { pointer.EndTime = DateTime.SpecifyKind(ep.EndTime.Value, DateTimeKind.Utc); } pointer.StepName = ep.StepName; pointer.RetryCount = ep.RetryCount; pointer.PredecessorId = ep.PredecessorId; pointer.ContextItem = JsonConvert.DeserializeObject(ep.ContextItem ?? string.Empty, SerializerSettings); if (!string.IsNullOrEmpty(ep.Children)) { pointer.Children = ep.Children.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); } pointer.EventName = ep.EventName; pointer.EventKey = ep.EventKey; pointer.EventPublished = ep.EventPublished; pointer.EventData = JsonConvert.DeserializeObject(ep.EventData ?? string.Empty, SerializerSettings); pointer.Outcome = JsonConvert.DeserializeObject(ep.Outcome ?? string.Empty, SerializerSettings); pointer.Status = ep.Status; if (!string.IsNullOrEmpty(ep.Scope)) { pointer.Scope = new List <string>(ep.Scope.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); } foreach (var attr in ep.ExtensionAttributes) { pointer.ExtensionAttributes[attr.AttributeKey] = JsonConvert.DeserializeObject(attr.AttributeValue, SerializerSettings); } result.ExecutionPointers.Add(pointer); } return(result); }
private void SelectErrorStrategy(WorkflowErrorHandling errorOption, WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer pointer, WorkflowStep step) { switch (errorOption) { case WorkflowErrorHandling.Retry: pointer.RetryCount++; pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(step.RetryInterval ?? def.DefaultErrorRetryInterval ?? _options.ErrorRetryInterval); step.PrimeForRetry(pointer); break; case WorkflowErrorHandling.Suspend: workflow.Status = WorkflowStatus.Suspended; break; case WorkflowErrorHandling.Terminate: workflow.Status = WorkflowStatus.Terminated; break; case WorkflowErrorHandling.Compensate: Compensate(workflow, def, pointer); break; } }
public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer exceptionPointer, WorkflowStep exceptionStep, Exception exception, Queue <ExecutionPointer> bubbleUpQueue) { var scope = new Stack <string>(exceptionPointer.Scope.Reverse()); scope.Push(exceptionPointer.Id); while (scope.Any()) { var pointerId = scope.Pop(); var scopePointer = workflow.ExecutionPointers.FindById(pointerId); var scopeStep = def.Steps.FindById(scopePointer.StepId); var resume = true; var revert = false; var txnStack = new Stack <string>(scope.Reverse()); while (txnStack.Count > 0) { var parentId = txnStack.Pop(); var parentPointer = workflow.ExecutionPointers.FindById(parentId); var parentStep = def.Steps.FindById(parentPointer.StepId); if ((!parentStep.ResumeChildrenAfterCompensation) || (parentStep.RevertChildrenAfterCompensation)) { resume = parentStep.ResumeChildrenAfterCompensation; revert = parentStep.RevertChildrenAfterCompensation; break; } } if ((scopeStep.ErrorBehavior ?? WorkflowErrorHandling.Compensate) != WorkflowErrorHandling.Compensate) { bubbleUpQueue.Enqueue(scopePointer); continue; } scopePointer.Active = false; scopePointer.EndTime = _datetimeProvider.UtcNow; scopePointer.Status = PointerStatus.Failed; if (scopeStep.CompensationStepId.HasValue) { scopePointer.Status = PointerStatus.Compensated; var compensationPointer = _pointerFactory.BuildCompensationPointer(def, scopePointer, exceptionPointer, scopeStep.CompensationStepId.Value); workflow.ExecutionPointers.Add(compensationPointer); if (resume) { foreach (var outcomeTarget in scopeStep.Outcomes.Where(x => x.Matches(workflow.Data))) { workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, scopePointer, outcomeTarget)); } } } if (revert) { var prevSiblings = workflow.ExecutionPointers .Where(x => scopePointer.Scope.SequenceEqual(x.Scope) && x.Id != scopePointer.Id && x.Status == PointerStatus.Complete) .OrderByDescending(x => x.EndTime) .ToList(); foreach (var siblingPointer in prevSiblings) { var siblingStep = def.Steps.FindById(siblingPointer.StepId); if (siblingStep.CompensationStepId.HasValue) { var compensationPointer = _pointerFactory.BuildCompensationPointer(def, siblingPointer, exceptionPointer, siblingStep.CompensationStepId.Value); workflow.ExecutionPointers.Add(compensationPointer); siblingPointer.Status = PointerStatus.Compensated; } } } } }
private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowExecutorResult wfResult, WorkflowDefinition def, CancellationToken cancellationToken = default) { IStepExecutionContext context = new StepExecutionContext { Workflow = workflow, Step = step, PersistenceData = pointer.PersistenceData, ExecutionPointer = pointer, Item = pointer.ContextItem, CancellationToken = cancellationToken }; using (var scope = _scopeProvider.CreateScope(context)) { _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); IStepBody body = step.ConstructBody(scope.ServiceProvider); var stepExecutor = scope.ServiceProvider.GetRequiredService <IStepExecutor>(); if (body == null) { _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); pointer.SleepUntil = _datetimeProvider.UtcNow.Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.UtcNow, Message = $"Unable to construct step body {step.BodyType}" }); return; } foreach (var input in step.Inputs) { input.AssignInput(workflow.Data, body, context); } switch (step.BeforeExecute(wfResult, context, pointer, body)) { case ExecutionPipelineDirective.Defer: return; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.UtcNow; return; } var result = await stepExecutor.ExecuteStep(context, body); if (result.Proceed) { foreach (var output in step.Outputs) { output.AssignOutput(workflow.Data, body, context); } } _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); step.AfterExecute(wfResult, context, result, pointer); } }
public override void AfterWorkflowIteration(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { base.AfterWorkflowIteration(executorResult, defintion, workflow, executionPointer); var func = _cancelCondition.Compile(); if (func((TData)workflow.Data)) { executionPointer.EndTime = DateTime.Now.ToUniversalTime(); executionPointer.Active = false; } }