예제 #1
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
 public override ExecutionPipelineDirective InitForExecution(
     WorkflowExecutorResult executorResult,
     WorkflowDefinition defintion,
     WorkflowInstance workflow,
     ExecutionPointer executionPointer)
 {
     return(ExecutionPipelineDirective.EndWorkflow);
 }
예제 #9
0
        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;
            }
        }
예제 #10
0
        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;
            }
        }
예제 #11
0
        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();
                    }
                }
            }
        }
예제 #12
0
 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);
 }
예제 #13
0
 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);
                }
            }
        }
예제 #16
0
        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);
        }
예제 #17
0
        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();
        }
예제 #18
0
        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");
        }
예제 #19
0
        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);
        }
예제 #20
0
        /// <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);
                }
            }
        }
예제 #21
0
        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));
                    }
                }
            }
        }
예제 #22
0
        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);
        }
예제 #23
0
        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);
            }
        }
예제 #24
0
        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;
                        }
                    }
                }
            }
        }
예제 #26
0
        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);
        }
예제 #27
0
        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);
            }
        }
예제 #28
0
        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));
                    }
                }
            }
        }