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.First(x => x.Id == 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.First(x => x.Id == 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.Now.ToUniversalTime(); 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.GetValue(workflow.Data) == null)) { 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.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 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).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; } } } } }