private async Task ExecuteOutOfProcBatch()
        {
            object outOfProcResults = null;

            Task invokeTask = this.FunctionInvocationCallback();

            if (invokeTask is Task <object> resultTask)
            {
                outOfProcResults = await resultTask;
            }
            else
            {
                throw new InvalidOperationException("The WebJobs runtime returned a invocation task that does not support return values!");
            }

            var jObj = outOfProcResults as JObject;

            if (jObj == null)
            {
                throw new ArgumentException("Out of proc orchestrators must return a valid JSON schema.");
            }

            var outOfProcResult = jObj.ToObject <OutOfProcResult>();

            // update the state
            this.context.State.EntityExists = outOfProcResult.EntityExists;
            this.context.State.EntityState  = outOfProcResult.EntityState;

            // for each operation, emit trace and send response message (if not a signal)
            for (int i = 0; i < this.OperationBatch.Count; i++)
            {
                var request = this.OperationBatch[i];
                var result  = outOfProcResult.Results[i];

                if (!result.IsError)
                {
                    this.Config.TraceHelper.OperationCompleted(
                        this.context.HubName,
                        this.context.Name,
                        this.context.InstanceId,
                        request.Id.ToString(),
                        request.Operation,
                        this.Config.GetIntputOutputTrace(request.Input),
                        this.Config.GetIntputOutputTrace(result.Result),
                        result.DurationInMilliseconds,
                        isReplay: false);
                }
                else
                {
                    this.context.CaptureApplicationError(new OperationErrorException(
                                                             $"Error in operation '{request.Operation}': {result}"));

                    this.Config.TraceHelper.OperationFailed(
                        this.context.HubName,
                        this.context.Name,
                        this.context.InstanceId,
                        request.Id.ToString(),
                        request.Operation,
                        this.Config.GetIntputOutputTrace(request.Input),
                        this.Config.GetIntputOutputTrace(result.Result),
                        result.DurationInMilliseconds,
                        isReplay: false);
                }

                if (!request.IsSignal)
                {
                    var target = new OrchestrationInstance()
                    {
                        InstanceId  = request.ParentInstanceId,
                        ExecutionId = request.ParentExecutionId,
                    };
                    var responseMessage = new ResponseMessage()
                    {
                        Result        = result.Result,
                        ExceptionType = result.IsError ? "Error" : null,
                    };
                    this.context.SendResponseMessage(target, request.Id, responseMessage, !result.IsError);
                }
            }

            // send signal messages
            foreach (var signal in outOfProcResult.Signals)
            {
                var request = new RequestMessage()
                {
                    ParentInstanceId  = this.context.InstanceId,
                    ParentExecutionId = null, // for entities, message sorter persists across executions
                    Id        = Guid.NewGuid(),
                    IsSignal  = true,
                    Operation = signal.Name,
                    Input     = signal.Input,
                };
                var target = new OrchestrationInstance()
                {
                    InstanceId = EntityId.GetSchedulerIdFromEntityId(signal.Target),
                };
                this.context.SendOperationMessage(target, request);
            }
        }
예제 #2
0
        internal void SendOutbox(OrchestrationContext innerContext, bool writeBackSuccessful, ResponseMessage serializationErrorMessage)
        {
            lock (this.outbox)
            {
                foreach (var message in this.outbox)
                {
                    if (message is LockMessage lockMessage)
                    {
                        this.Config.TraceHelper.SendingEntityMessage(
                            this.InstanceId,
                            this.ExecutionId,
                            lockMessage.Target.InstanceId,
                            lockMessage.EventName,
                            lockMessage.EventContent);

                        innerContext.SendEvent(lockMessage.Target, lockMessage.EventName, lockMessage.EventContent);
                    }
                    else if (message is ResultMessage resultMessage)
                    {
                        // non-error result messages are replaced with the writeback failed response
                        if (!writeBackSuccessful && !resultMessage.IsError)
                        {
                            resultMessage.EventContent = serializationErrorMessage;
                        }

                        this.Config.TraceHelper.SendingEntityMessage(
                            this.InstanceId,
                            this.ExecutionId,
                            resultMessage.Target.InstanceId,
                            resultMessage.EventName,
                            resultMessage.EventContent);

                        innerContext.SendEvent(resultMessage.Target, resultMessage.EventName, resultMessage.EventContent);
                    }
                    else if (!writeBackSuccessful)
                    {
                        // all other messages (signals and fire-and-forget) are suppressed if the writeback failed
                        // this helps to keep the observer pattern correct, for example.
                    }
                    else if (message is OperationMessage operationMessage)
                    {
                        this.Config.TraceHelper.SendingEntityMessage(
                            this.InstanceId,
                            this.ExecutionId,
                            operationMessage.Target.InstanceId,
                            operationMessage.EventName,
                            operationMessage.EventContent);

                        innerContext.SendEvent(operationMessage.Target, operationMessage.EventName, operationMessage.EventContent);
                    }
                    else if (message is FireAndForgetMessage fireAndForgetMessage)
                    {
                        var dummyTask = innerContext.CreateSubOrchestrationInstance <object>(
                            fireAndForgetMessage.FunctionName,
                            DurableOrchestrationContext.DefaultVersion,
                            fireAndForgetMessage.InstanceId,
                            fireAndForgetMessage.Input,
                            new Dictionary <string, string>()
                        {
                            { OrchestrationTags.FireAndForget, "" }
                        });

                        System.Diagnostics.Debug.Assert(dummyTask.IsCompleted, "task should be fire-and-forget");
                    }
                }

                this.outbox.Clear();
            }
        }
        public override async Task <string> Execute(OrchestrationContext innerContext, string serializedInput)
        {
#if !FUNCTIONS_V1
            // Adding "Tags" to activity allows using App Insights to query current state of entities
            var activity = Activity.Current;
            OrchestrationRuntimeStatus status = OrchestrationRuntimeStatus.Running;

            DurableTaskExtension.TagActivityWithOrchestrationStatus(status, this.context.InstanceId, true);
#endif

            if (this.operationBatch.Count == 0 &&
                this.lockRequest == null &&
                (this.toBeRescheduled == null || this.toBeRescheduled.Count == 0))
            {
                // we are idle after a ContinueAsNew - the batch is empty.
                // Wait for more messages to get here (via extended sessions)
                await this.doneProcessingMessages.Task;
            }

            if (!this.messageDataConverter.IsDefault)
            {
                innerContext.MessageDataConverter = this.messageDataConverter;
            }

            if (!this.errorDataConverter.IsDefault)
            {
                innerContext.ErrorDataConverter = this.errorDataConverter;
            }

            this.Config.TraceHelper.FunctionStarting(
                this.context.HubName,
                this.context.Name,
                this.context.InstanceId,
                this.Config.GetIntputOutputTrace(serializedInput),
                FunctionType.Entity,
                isReplay: false);

            if (this.NumberEventsToReceive > 0)
            {
                await this.doneProcessingMessages.Task;
            }

            // Commit the effects of this batch, if
            // we have not already run into an internal error
            // (in which case we will abort the batch instead of committing it)
            if (this.context.InternalError == null)
            {
                bool            writeBackSuccessful       = true;
                ResponseMessage serializationErrorMessage = null;

                if (this.RollbackFailedOperations)
                {
                    // the state has already been written back, since it is
                    // done right after each operation.
                }
                else
                {
                    // we are writing back the state here, after executing
                    // the entire batch of operations.
                    writeBackSuccessful = this.context.TryWriteback(out serializationErrorMessage);
                }

                // Reschedule all signals that were received before their time
                this.context.RescheduleMessages(innerContext, this.toBeRescheduled);

                // Send all buffered outgoing messages
                this.context.SendOutbox(innerContext, writeBackSuccessful, serializationErrorMessage);

                var jstate = JToken.FromObject(this.context.State);

                // continue as new
                innerContext.ContinueAsNew(jstate);
            }

            if (this.context.ErrorsPresent(out var description))
            {
                this.Config.TraceHelper.FunctionFailed(
                    this.context.HubName,
                    this.context.Name,
                    this.context.InstanceId,
                    description,
                    functionType: FunctionType.Entity,
                    isReplay: false);
            }
            else
            {
                this.Config.TraceHelper.FunctionCompleted(
                    this.context.HubName,
                    this.context.Name,
                    this.context.InstanceId,
                    this.Config.GetIntputOutputTrace(this.context.State.EntityState),
                    continuedAsNew: true,
                    functionType: FunctionType.Entity,
                    isReplay: false);
            }

            // The return value is not used.
            return(string.Empty);
        }
예제 #4
0
        internal void SendOutbox(OrchestrationContext innerContext, bool writeBackSuccessful, ResponseMessage serializationErrorMessage)
        {
            lock (this.outbox)
            {
                foreach (var message in this.outbox)
                {
                    if (message is LockMessage lockMessage)
                    {
                        this.Config.TraceHelper.SendingEntityMessage(
                            this.InstanceId,
                            this.ExecutionId,
                            lockMessage.Target.InstanceId,
                            lockMessage.EventName,
                            lockMessage.EventContent);

                        innerContext.SendEvent(lockMessage.Target, lockMessage.EventName, lockMessage.EventContent);
                    }
                    else if (message is ResultMessage resultMessage)
                    {
                        // Since for all of the operations in the batch, their effect on the entity state
                        // is lost, we don't want the calling orchestrations to think everything is o.k.
                        // They should be notified, so we replace all non-error operation results
                        // with an exception result.
                        if (!writeBackSuccessful && !resultMessage.IsError)
                        {
                            resultMessage.EventContent = serializationErrorMessage;
                        }

                        this.Config.TraceHelper.SendingEntityMessage(
                            this.InstanceId,
                            this.ExecutionId,
                            resultMessage.Target.InstanceId,
                            resultMessage.EventName,
                            resultMessage.EventContent);

                        innerContext.SendEvent(resultMessage.Target, resultMessage.EventName, resultMessage.EventContent);
                    }
                    else if (!writeBackSuccessful)
                    {
                        // all other messages (signals and fire-and-forget) are suppressed if the writeback failed
                        // this helps to keep the observer pattern correct, for example.
                    }
                    else if (message is OperationMessage operationMessage)
                    {
                        if (!operationMessage.EventContent.ScheduledTime.HasValue)
                        {
                            this.State.MessageSorter.LabelOutgoingMessage(operationMessage.EventContent, operationMessage.Target.InstanceId, DateTime.UtcNow, this.EntityMessageReorderWindow);
                        }

                        this.Config.TraceHelper.SendingEntityMessage(
                            this.InstanceId,
                            this.ExecutionId,
                            operationMessage.Target.InstanceId,
                            operationMessage.EventName,
                            operationMessage.EventContent);

                        innerContext.SendEvent(operationMessage.Target, operationMessage.EventName, operationMessage.EventContent);
                    }
                    else if (message is FireAndForgetMessage fireAndForgetMessage)
                    {
                        var dummyTask = innerContext.CreateSubOrchestrationInstance <object>(
                            fireAndForgetMessage.FunctionName,
                            DurableOrchestrationContext.DefaultVersion,
                            fireAndForgetMessage.InstanceId,
                            fireAndForgetMessage.Input,
                            new Dictionary <string, string>()
                        {
                            { OrchestrationTags.FireAndForget, "" }
                        });

                        System.Diagnostics.Debug.Assert(dummyTask.IsCompleted, "task should be fire-and-forget");
                    }
                }

                this.outbox.Clear();
            }
        }