protected override async Task ProcessItem(string itemId, CancellationToken cancellationToken) { if (await _lockProvider.AcquireLock(itemId, cancellationToken)) { WorkflowInstance workflow = null; WorkflowExecutorResult result = null; var persistenceStore = _persistenceStorePool.Get(); try { try { cancellationToken.ThrowIfCancellationRequested(); workflow = await persistenceStore.GetWorkflowInstance(itemId); if (workflow.Status == WorkflowStatus.Runnable) { var executor = _executorPool.Get(); try { result = await executor.Execute(workflow, Options); } finally { _executorPool.Return(executor); await persistenceStore.PersistWorkflow(workflow); } } } finally { await _lockProvider.ReleaseLock(itemId); if ((workflow != null) && (result != null)) { foreach (var sub in result.Subscriptions) { await SubscribeEvent(sub, persistenceStore); } await persistenceStore.PersistErrors(result.Errors); var readAheadTicks = _datetimeProvider.Now.Add(Options.PollInterval).ToUniversalTime().Ticks; if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < readAheadTicks) { new Task(() => FutureQueue(workflow, cancellationToken)).Start(); } } } } finally { _persistenceStorePool.Return(persistenceStore); } } else { Logger.LogInformation("Workflow locked {0}", itemId); } }
public async Task <WorkflowExecutorResult> ExecuteUntilEventWait(WorkflowInstance workflow, Func <WorkflowExecutorResult, Task> stepExecutedAction) { WorkflowExecutorResult wfResult = null; try { //executing flow wfResult = await Execute(workflow); } finally { await stepExecutedAction?.Invoke(wfResult); } if (workflow.Status == WorkflowStatus.Runnable && workflow.NextExecution.HasValue && !wfResult.HasSubscription && !wfResult.HasError) { var target = (workflow.NextExecution.Value - _datetimeProvider.Now.ToUniversalTime().Ticks); if (target > 0) { //TODO maybe cancellationToken can be used await Task.Delay(TimeSpan.FromTicks(target)); } wfResult = await this.ExecuteUntilEventWait(workflow, stepExecutedAction); } return(wfResult); }
public void should_advance_workflow() { //arrange var definition = new WorkflowDefinition(); var pointer1 = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; var pointer2 = new ExecutionPointer(); var outcome = new StepOutcome() { NextStep = 1 }; var step = A.Fake <WorkflowStep>(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer1); var result = ExecutionResult.Next(); A.CallTo(() => step.Outcomes).Returns(new List <StepOutcome>() { outcome }); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome)).Returns(pointer2); //act Subject.ProcessExecutionResult(instance, definition, pointer1, step, result, workflowResult); //assert pointer1.Active.Should().BeFalse(); pointer1.Status.Should().Be(PointerStatus.Complete); pointer1.EndTime.Should().NotBeNull(); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome)).MustHaveHappened(); instance.ExecutionPointers.Should().Contain(pointer2); }
public void should_branch_children() { //arrange var branch = 10; var child = 2; var definition = new WorkflowDefinition(); var pointer = new ExecutionPointer() { Active = true, StepId = 0, Status = PointerStatus.Running }; var childPointer = new ExecutionPointer(); var step = A.Fake <WorkflowStep>(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); var result = ExecutionResult.Branch(new List <object>() { branch }, null); A.CallTo(() => step.Children).Returns(new List <int>() { child }); A.CallTo(() => PointerFactory.BuildChildPointer(definition, pointer, child, branch)).Returns(childPointer); //act Subject.ProcessExecutionResult(instance, definition, pointer, step, result, workflowResult); //assert A.CallTo(() => PointerFactory.BuildChildPointer(definition, pointer, child, branch)).MustHaveHappened(); instance.ExecutionPointers.Should().Contain(childPointer); }
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) { 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); }
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.Now.ToUniversalTime(); return(false); } if (pointer.Status != PointerStatus.Running) { pointer.Status = PointerStatus.Running; _publisher.PublishNotification(new StepStarted() { EventTimeUtc = _datetimeProvider.Now, 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.Now.ToUniversalTime(); } return(true); }
public override ExecutionPipelineDirective InitForExecution( WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { return(ExecutionPipelineDirective.EndWorkflow); }
public override void AfterWorkflowIteration(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer) { base.AfterWorkflowIteration(executorResult, defintion, workflow, executionPointer); var taskStep = workflow.ExecutionPointers.FindById(executionPointer.PredecessorId); if (taskStep.EventPublished) { executionPointer.EndTime = DateTime.Now.ToUniversalTime(); executionPointer.Active = false; } }
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 = Clock.Now.ToUniversalTime(); executionPointer.Active = false; } }
protected override async Task ProcessItem(string itemId, CancellationToken cancellationToken) { if (!await _lockProvider.AcquireLock(itemId, cancellationToken)) { Logger.LogInformation("Workflow locked {0}", itemId); return; } WorkflowInstance workflow = null; WorkflowExecutorResult result = null; try { cancellationToken.ThrowIfCancellationRequested(); workflow = await _persistenceStore.GetWorkflowInstance(itemId, cancellationToken); if (workflow.Status == WorkflowStatus.Runnable) { try { result = await _executor.Execute(workflow, cancellationToken); } finally { await _persistenceStore.PersistWorkflow(workflow, cancellationToken); await QueueProvider.QueueWork(itemId, QueueType.Index); _greylist.Remove($"wf:{itemId}"); } } } finally { await _lockProvider.ReleaseLock(itemId); if ((workflow != null) && (result != null)) { foreach (var sub in result.Subscriptions) { await SubscribeEvent(sub, _persistenceStore, cancellationToken); } await _persistenceStore.PersistErrors(result.Errors, cancellationToken); var readAheadTicks = _datetimeProvider.UtcNow.Add(Options.PollInterval).Ticks; if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < readAheadTicks) { new Task(() => FutureQueue(workflow, cancellationToken)).Start(); } } } }
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 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 void should_select_correct_outcomes() { //arrange var definition = new WorkflowDefinition(); var pointer1 = new ExecutionPointer() { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var pointer2 = new ExecutionPointer() { Id = "2" }; var pointer3 = new ExecutionPointer() { Id = "3" }; Expression <Func <object, object> > expr1 = data => 10; Expression <Func <object, object> > expr2 = data => 20; var outcome1 = new ValueOutcome() { NextStep = 1, Value = expr1 }; var outcome2 = new ValueOutcome() { NextStep = 2, Value = expr2 }; var step = A.Fake <WorkflowStep>(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer1); var result = ExecutionResult.Outcome(20); A.CallTo(() => step.Outcomes).Returns(new List <IStepOutcome>() { outcome1, outcome2 }); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome1)).Returns(pointer2); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome2)).Returns(pointer3); //act Subject.ProcessExecutionResult(instance, definition, pointer1, step, result, workflowResult); //assert pointer1.Active.Should().BeFalse(); pointer1.Status.Should().Be(PointerStatus.Complete); pointer1.EndTime.Should().NotBeNull(); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome1)).MustNotHaveHappened(); A.CallTo(() => PointerFactory.BuildNextPointer(definition, pointer1, outcome2)).MustHaveHappened(); instance.ExecutionPointers.Should().NotContain(pointer2); instance.ExecutionPointers.Should().Contain(pointer3); }
internal static void Enrich(WorkflowExecutorResult result) { var activity = Activity.Current; if (activity != null) { activity.SetTag("workflow.subscriptions.count", result.Subscriptions.Count); activity.SetTag("workflow.errors.count", result.Errors.Count); if (result.Errors.Count > 0) { activity.SetStatus(Status.Error); activity.SetStatus(ActivityStatusCode.Error); } } }
public void should_set_persistence_data() { //arrange var persistenceData = new object(); var definition = new WorkflowDefinition(); var pointer = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake <WorkflowStep>(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); var result = ExecutionResult.Persist(persistenceData); //act Subject.ProcessExecutionResult(instance, definition, pointer, step, result, workflowResult); //assert pointer.PersistenceData.Should().Be(persistenceData); }
public void should_sleep_pointer() { //arrange var persistenceData = new object(); var definition = new WorkflowDefinition(); var pointer = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake <WorkflowStep>(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); var result = ExecutionResult.Sleep(TimeSpan.FromMinutes(5), persistenceData); //act Subject.ProcessExecutionResult(instance, definition, pointer, step, result, workflowResult); //assert pointer.Status.Should().Be(PointerStatus.Sleeping); pointer.SleepUntil.Should().NotBeNull(); }
public void should_subscribe_to_event() { //arrange var definition = new WorkflowDefinition(); var pointer = new ExecutionPointer { Id = "1", Active = true, StepId = 0, Status = PointerStatus.Running }; var step = A.Fake <WorkflowStep>(); var workflowResult = new WorkflowExecutorResult(); var instance = GivenWorkflow(pointer); var result = ExecutionResult.WaitForEvent("Event", "Key", DateTime.Now); //act Subject.ProcessExecutionResult(instance, definition, pointer, step, result, workflowResult); //assert pointer.Status.Should().Be(PointerStatus.WaitingForEvent); pointer.Active.Should().BeFalse(); pointer.EventName.Should().Be("Event"); pointer.EventKey.Should().Be("Key"); workflowResult.Subscriptions.Should().Contain(x => x.StepId == pointer.StepId && x.EventName == "Event" && x.EventKey == "Key"); }
public async Task <WorkflowExecutorResult> Execute(WorkflowInstance workflow) { var wfResult = new WorkflowExecutorResult(); var exePointers = new List <ExecutionPointer>(workflow.ExecutionPointers.Where(x => x.Active && (!x.SleepUntil.HasValue || x.SleepUntil < _datetimeProvider.Now.ToUniversalTime()))); var def = _registry.GetDefinition(workflow.WorkflowDefinitionId, workflow.Version); if (def == null) { _logger.LogError("Workflow {0} version {1} is not registered", workflow.WorkflowDefinitionId, workflow.Version); return(wfResult); } foreach (var pointer in exePointers) { if (pointer.Status == PointerStatus.Cancelled) { continue; } var step = def.Steps.FindById(pointer.StepId); if (step == null) { _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = String.Format("Unable to find step {0} in workflow definition", pointer.StepId) }); continue; } try { switch (step.InitForExecution(wfResult, def, workflow, pointer)) { case ExecutionPipelineDirective.Defer: continue; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); continue; } if (pointer.Status != PointerStatus.Running) { pointer.Status = PointerStatus.Running; _publisher.PublishNotification(new StepStarted() { EventTimeUtc = _datetimeProvider.Now, 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.Now.ToUniversalTime(); } 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 = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) }); continue; } 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: continue; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); continue; } 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); } } catch (Exception ex) { _logger.LogError("Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = ex.Message }); _executionResultProcessor.HandleStepException(workflow, def, pointer, step, ex); Host.ReportStepError(workflow, step, ex); } _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); } ProcessAfterExecutionIteration(workflow, def, wfResult); DetermineNextExecutionTime(workflow); return(wfResult); }
/// <summary> /// Worker thread body /// </summary> private async void RunWorkflows() { while (!_shutdown) { try { var workflowId = await _queueProvider.DequeueWork(QueueType.Workflow); if (workflowId != null) { try { if (await _lockProvider.AcquireLock(workflowId)) { WorkflowInstance workflow = null; WorkflowExecutorResult result = null; try { workflow = await _persistenceStore.GetWorkflowInstance(workflowId); if (workflow.Status == WorkflowStatus.Runnable) { try { result = _executor.Execute(workflow, _options); } finally { await _persistenceStore.PersistWorkflow(workflow); } } } finally { await _lockProvider.ReleaseLock(workflowId); if ((workflow != null) && (result != null)) { foreach (var sub in result.Subscriptions) { await SubscribeEvent(sub); } await _persistenceStore.PersistErrors(result.Errors); if ((workflow.Status == WorkflowStatus.Runnable) && workflow.NextExecution.HasValue && workflow.NextExecution.Value < DateTime.Now.ToUniversalTime().Ticks) { await _queueProvider.QueueWork(workflowId, QueueType.Workflow); } } } } else { _logger.LogInformation("Workflow locked {0}", workflowId); } } catch (Exception ex) { _logger.LogError(ex.Message); } } else { await Task.Delay(_options.IdleTime); //no work } } catch (Exception ex) { _logger.LogError(ex.Message); } } }
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)); } _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 async Task <WorkflowExecutorResult> Execute(WorkflowInstance workflow, WorkflowOptions options) { var wfResult = new WorkflowExecutorResult(); var exePointers = new List <ExecutionPointer>(workflow.ExecutionPointers.Where(x => x.Active && (!x.SleepUntil.HasValue || x.SleepUntil < _datetimeProvider.Now.ToUniversalTime()))); var def = _registry.GetDefinition(workflow.WorkflowDefinitionId, workflow.Version); if (def == null) { _logger.LogError("Workflow {0} version {1} is not registered", workflow.WorkflowDefinitionId, workflow.Version); return(wfResult); } foreach (var pointer in exePointers) { var step = def.Steps.First(x => x.Id == pointer.StepId); if (step != null) { try { switch (step.InitForExecution(wfResult, def, workflow, pointer)) { case ExecutionPipelineDirective.Defer: continue; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); continue; } if (!pointer.StartTime.HasValue) { pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); } _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); IStepBody body = step.ConstructBody(_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 = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) }); continue; } IStepExecutionContext context = new StepExecutionContext() { Workflow = workflow, Step = step, PersistenceData = pointer.PersistenceData, ExecutionPointer = pointer, Item = pointer.ContextItem }; ProcessInputs(workflow, step, body, context); switch (step.BeforeExecute(wfResult, context, pointer, body)) { case ExecutionPipelineDirective.Defer: continue; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); continue; } var result = await body.RunAsync(context); if (result.Proceed) { ProcessOutputs(workflow, step, body); } ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); step.AfterExecute(wfResult, context, result, pointer); } catch (Exception ex) { pointer.RetryCount++; _logger.LogError("Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = ex.Message }); switch (step.ErrorBehavior ?? def.DefaultErrorBehavior) { case WorkflowErrorHandling.Retry: pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(step.RetryInterval ?? def.DefaultErrorRetryInterval ?? options.ErrorRetryInterval); break; case WorkflowErrorHandling.Suspend: workflow.Status = WorkflowStatus.Suspended; break; case WorkflowErrorHandling.Terminate: workflow.Status = WorkflowStatus.Terminated; break; } Host.ReportStepError(workflow, step, ex); } } else { _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = String.Format("Unable to find step {0} in workflow definition", pointer.StepId) }); } } ProcessAfterExecutionIteration(workflow, def, wfResult); DetermineNextExecutionTime(workflow); return(wfResult); }
private void ProcessAfterExecutionIteration(WorkflowInstance workflow, WorkflowDefinition workflowDef, WorkflowExecutorResult workflowResult) { var pointers = workflow.ExecutionPointers.Where(x => x.EndTime == null); foreach (var pointer in pointers) { var step = workflowDef.Steps.First(x => x.Id == pointer.StepId); step?.AfterWorkflowIteration(workflowResult, workflowDef, workflow, pointer); } }
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 = _datetimeProvider.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 = _datetimeProvider.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 ProcessCancellations(WorkflowInstance workflow, WorkflowDefinition workflowDef, WorkflowExecutorResult executionResult) { foreach (var step in workflowDef.Steps.Where(x => x.CancelCondition != null)) { var func = step.CancelCondition.Compile(); var cancel = false; try { cancel = (bool)(func.DynamicInvoke(workflow.Data)); } catch (Exception ex) { _logger.LogError(default(EventId), ex, ex.Message); } if (cancel) { var toCancel = workflow.ExecutionPointers.Where(x => x.StepId == step.Id && x.Status != PointerStatus.Complete && x.Status != PointerStatus.Cancelled).ToList(); foreach (var ptr in toCancel) { if (step.ProceedOnCancel) { _executionResultProcessor.ProcessExecutionResult(workflow, workflowDef, ptr, step, ExecutionResult.Next(), executionResult); } ptr.EndTime = _dateTimeProvider.UtcNow; ptr.Active = false; ptr.Status = PointerStatus.Cancelled; foreach (var descendent in workflow.ExecutionPointers.FindByScope(ptr.Id).Where(x => x.Status != PointerStatus.Complete && x.Status != PointerStatus.Cancelled)) { descendent.EndTime = _dateTimeProvider.UtcNow; descendent.Active = false; descendent.Status = PointerStatus.Cancelled; } } } } }
public async Task <WorkflowExecutorResult> Execute(WorkflowInstance workflow) { var wfResult = new WorkflowExecutorResult(); var exePointers = new List <ExecutionPointer>(workflow.ExecutionPointers.Where(x => x.Active && (!x.SleepUntil.HasValue || x.SleepUntil < _datetimeProvider.Now.ToUniversalTime()))); var def = _registry.GetDefinition(workflow.WorkflowDefinitionId, workflow.Version); if (def == null) { _logger.LogError("Workflow {0} version {1} is not registered", workflow.WorkflowDefinitionId, workflow.Version); return(wfResult); } _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); foreach (var pointer in exePointers) { if (!pointer.Active) { continue; } var step = def.Steps.FindById(pointer.StepId); if (step == null) { _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); 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 find step {pointer.StepId} in workflow definition" }); continue; } try { if (!InitializeStep(workflow, step, wfResult, def, pointer)) { continue; } await ExecuteStep(workflow, step, pointer, wfResult, def); } catch (Exception ex) { _logger.LogError("Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = ex.Message }); _executionResultProcessor.HandleStepException(workflow, def, pointer, step, ex); Host.ReportStepError(workflow, step, ex); } _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); } ProcessAfterExecutionIteration(workflow, def, wfResult); DetermineNextExecutionTime(workflow); return(wfResult); }
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(); 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); } }
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)); } } } }