示例#1
0
文件: Database.cs 项目: malooba/flow
        /// <summary>
        /// Handle response for a task including heartbeats
        /// The returned status property identifies the type of response
        /// </summary>
        /// <param name="taskToken"></param>
        /// <param name="json"></param>
        public string ProcessTaskResponse(Guid taskToken, string json)
        {
            try
            {
                var taskResponse = JsonConvert.DeserializeObject <ActivityTaskResponse>(json);
                // Note that the task may have been deleted by the ActivityTaskTimeoutChecker
                var    task     = TaskLists.SingleOrDefault(q => q.TaskToken == taskToken);
                string response = null;

                int i;
                for (i = 0; i < RETRIES; i++)
                {
                    switch (taskResponse.Status.ToLower())
                    {
                    case "success":
                        if (task == null)
                        {
                            log.Info($"Ignoring success response for deleted task with token: {taskToken}");
                            break;
                        }
                        var result = taskResponse.Result;

                        var atce = new ActivityTaskCompletedEvent
                        {
                            Result            = result,
                            SchedulingEventId = task.TaskScheduledEventId,
                        };

                        InsertHistory(this, task.Execution, atce);
                        task.Execution.AwaitingDecision = true;
                        TaskLists.DeleteOnSubmit(task);
                        break;

                    case "failure":
                        if (task == null)
                        {
                            log.Info($"Ignoring failure response for deleted task with token: {taskToken}");
                            break;
                        }
                        var atfe = new ActivityTaskFailedEvent
                        {
                            Reason            = taskResponse.Reason,
                            Details           = taskResponse.Details,
                            SchedulingEventId = task.TaskScheduledEventId
                        };
                        InsertHistory(this, task.Execution, atfe);
                        task.Execution.AwaitingDecision = true;
                        TaskLists.DeleteOnSubmit(task);
                        break;

                    case "cancelled":
                        if (task == null)
                        {
                            log.Info($"Ignoring cancellation response for deleted task with token: {taskToken}");
                            break;
                        }
                        var atxe = new ActivityTaskCancelledEvent
                        {
                            SchedulingEventId = task.TaskScheduledEventId
                        };
                        InsertHistory(this, task.Execution, atxe);
                        task.Execution.AwaitingDecision = true;
                        TaskLists.DeleteOnSubmit(task);
                        break;

                    case "heartbeat":
                        if (task != null)
                        {
                            if (taskResponse.Progress.HasValue)
                            {
                                task.Progress = taskResponse.Progress.Value;
                            }

                            if (taskResponse.ProgressMessage != null)
                            {
                                task.ProgressMessage = taskResponse.ProgressMessage;
                            }

                            if (task.HeartbeatTimeout.HasValue)
                            {
                                task.HeartbeatAlarm = DateTime.UtcNow.AddSeconds(task.HeartbeatTimeout.Value);
                            }


                            // Send a progress notification to the updater
                            var data = new JObject(
                                new JProperty("type", "ActivityTaskHeartbeat"),
                                new JProperty("progress", task.Progress ?? -1),
                                new JProperty("message", task.ProgressMessage ?? ""),
                                new JProperty("progressData", JToken.Parse(task.ProgressData)));

                            var qt = CreateUpdaterNotification(task.Execution, data);
                            TaskLists.InsertOnSubmit(qt);
                            SubmitChanges();
                        }
                        else
                        {
                            log.Info($"Received heartbeat for deleted task with token: {taskToken}, cancelling");
                        }
                        // If the task has been cancelled or deleted then send a cancellation request
                        response = new JObject {
                            new JProperty("cancellationRequested", task == null || task.Cancelling)
                        }.ToString(Formatting.None);
                        break;

                    case "rescheduled":
                        // Rescheduled tasks are left in the tasklist with an updated scheduling time
                        // effectively pushing them to the back of the queue
                        if (task != null)
                        {
                            task.ScheduledAt = DateTime.UtcNow;
                            task.WorkerId    = null;
                        }
                        break;

                    default:
                        log.ErrorFormat("Invalid activity task response status: {0}", taskResponse.Status);
                        break;
                    }

                    // Nothing to update if the task has been deleted
                    if (task == null)
                    {
                        return(response);
                    }

                    try
                    {
                        SubmitChanges();
                        break;
                    }
                    catch (ChangeConflictException)
                    {
                        Refresh(RefreshMode.KeepCurrentValues, task.Execution);
                    }
                }
                if (i == RETRIES)
                {
                    log.Error($"Failed to process {taskResponse.Status} response from task {taskToken}");
                    return(null);
                }
                return(response);
            }
            catch (Exception ex)
            {
                log.Error("Failed to process activity task response", ex);
                return(null);
            }
        }
示例#2
0
文件: Decider.cs 项目: malooba/flow
        /// <summary>
        /// Process task completion by scheduling the next task or completing the workflow
        /// </summary>
        /// <param name="db"></param>
        /// <param name="workflow"></param>
        /// <param name="execution"></param>
        /// <param name="history"></param>
        /// <param name="decisions"></param>
        private static void ProcessActivityTaskCompletedEvent(Database db, WorkflowObj workflow, Execution execution, History history, List <History> decisions)
        {
            // There should(!) be no contention for the data modified in this process
            var evt           = ActivityTaskCompletedEvent.Create(history);
            var se            = ActivityTaskScheduledEvent.Create(db.GetHistoryEvent(execution.ExecutionId, evt.SchedulingEventId));
            var completedTask = workflow.Tasks.Single(t => t.TaskId == se.TaskId);

            // Default task outflow
            var outflow = "Out";

            // Update variables
            if (evt.Result != null)
            {
                // Update the variables if there are any normal results (not prefixed with "$")
                if (evt.Result.Properties().Any(p => !p.Name.StartsWith("$")))
                {
                    var variables = db.Variables.Where(v => v.Execution == execution).ToArray();
                    foreach (var o in completedTask.Outputs.Where(o => o.Value.Var != null))
                    {
                        // If the activity has not returned a value for an output then we don't update the mapped variable
                        // In general it is probably best if activities return values for all outputs to avoid confusion
                        JToken value;
                        if (evt.Result.TryGetValue(o.Key, out value))
                        {
                            variables.Single(v => v.Name == o.Value.Var).Json = value.ToString(Formatting.None);
                        }
                    }
                }
                // Get the correct outflow
                JToken outflowToken;
                if (evt.Result.TryGetValue("$outflow", out outflowToken))
                {
                    if (outflowToken.Type == JTokenType.String)
                    {
                        outflow = (string)outflowToken;
                    }
                    else
                    {
                        throw new ApplicationException("Task outflow identifier must be a string");
                    }
                }
            }

            var nextTaskId = completedTask.Outflows.Single(o => o.Name == outflow).Target;
            var nextTask   = workflow.Tasks.Single(t => t.TaskId == nextTaskId);

            // A task with no outflows is an end
            if (nextTask.Outflows.Length == 0)
            {
                Console.WriteLine($"Execution state = {execution.ExecutionState.State}");
                if (ExState.Create(execution.ExecutionState.State) != ExState.Cleanup)
                {
                    CreateWorkflowCompletedEvent(execution, decisions);
                }
                AttemptCleanup(db, execution, workflow, decisions);
            }
            else
            {
                CreateTaskScheduledEvent(db, execution, nextTask, decisions);
            }
        }