private async Task SignalEntityAsyncInternal(DurableClient durableClient, string hubName, EntityId entityId, DateTime?scheduledTimeUtc, string operationName, object operationInput)
        {
            var entityKey = entityId.EntityKey;

            if (entityKey.Any(IsInvalidCharacter))
            {
                throw new ArgumentException(nameof(entityKey), "Entity keys must not contain /, \\, #, ?, or control characters.");
            }

            if (operationName == null)
            {
                throw new ArgumentNullException(nameof(operationName));
            }

            if (scheduledTimeUtc.HasValue)
            {
                scheduledTimeUtc = scheduledTimeUtc.Value.ToUniversalTime();
            }

            if (this.ClientReferencesCurrentApp(durableClient))
            {
                this.config.ThrowIfFunctionDoesNotExist(entityId.EntityName, FunctionType.Entity);
            }

            var guid       = Guid.NewGuid(); // unique id for this request
            var instanceId = EntityId.GetSchedulerIdFromEntityId(entityId);
            var instance   = new OrchestrationInstance()
            {
                InstanceId = instanceId
            };
            var request = new RequestMessage()
            {
                ParentInstanceId  = null, // means this was sent by a client
                ParentExecutionId = null,
                Id            = guid,
                IsSignal      = true,
                Operation     = operationName,
                ScheduledTime = scheduledTimeUtc,
            };

            if (operationInput != null)
            {
                request.SetInput(operationInput, this.messageDataConverter);
            }

            var jrequest  = JToken.FromObject(request, this.messageDataConverter.JsonSerializer);
            var eventName = scheduledTimeUtc.HasValue
                ? EntityMessageEventNames.ScheduledRequestMessageEventName(request.GetAdjustedDeliveryTime(this.durabilityProvider))
                : EntityMessageEventNames.RequestMessageEventName;
            await durableClient.client.RaiseEventAsync(instance, eventName, jrequest);

            this.traceHelper.FunctionScheduled(
                hubName,
                entityId.EntityName,
                EntityId.GetSchedulerIdFromEntityId(entityId),
                reason: $"EntitySignal:{operationName}",
                functionType: FunctionType.Entity,
                isReplay: false);
        }
Exemplo n.º 2
0
 internal void SendResponseMessage(OrchestrationInstance target, Guid requestId, object message, bool isException)
 {
     lock (this.outbox)
     {
         this.outbox.Add(new ResultMessage()
         {
             Target       = target,
             EventName    = EntityMessageEventNames.ResponseMessageEventName(requestId),
             EventContent = message,
             IsError      = isException,
         });
     }
 }
Exemplo n.º 3
0
 internal void SendLockResponseMessage(OrchestrationInstance target, Guid requestId)
 {
     lock (this.outbox)
     {
         this.outbox.Add(new LockMessage()
         {
             Target       = target,
             EventName    = EntityMessageEventNames.ResponseMessageEventName(requestId),
             EventContent = new ResponseMessage()
             {
                 Result = "Lock Acquisition Completed", // ignored by receiver but shows up in traces
             },
         });
     }
 }
Exemplo n.º 4
0
        internal void RescheduleMessages(OrchestrationContext innerContext, List <RequestMessage> messages)
        {
            if (messages != null)
            {
                foreach (var message in messages)
                {
                    var instance = new OrchestrationInstance {
                        InstanceId = this.InstanceId
                    };
                    DateTime adjustedDeliveryTime = message.GetAdjustedDeliveryTime(this.durabilityProvider);
                    var      eventName            = EntityMessageEventNames.ScheduledRequestMessageEventName(adjustedDeliveryTime);
                    innerContext.SendEvent(instance, eventName, message);
                }

                messages.Clear();
            }
        }
        private async Task SignalEntityAsyncInternal(TaskHubClient client, string hubName, EntityId entityId, DateTime?scheduledTimeUtc, string operationName, object operationInput)
        {
            if (operationName == null)
            {
                throw new ArgumentNullException(nameof(operationName));
            }

            if (this.client.Equals(client))
            {
                this.config.ThrowIfFunctionDoesNotExist(entityId.EntityName, FunctionType.Entity);
            }

            var guid       = Guid.NewGuid(); // unique id for this request
            var instanceId = EntityId.GetSchedulerIdFromEntityId(entityId);
            var instance   = new OrchestrationInstance()
            {
                InstanceId = instanceId
            };
            var request = new RequestMessage()
            {
                ParentInstanceId  = null, // means this was sent by a client
                ParentExecutionId = null,
                Id            = guid,
                IsSignal      = true,
                Operation     = operationName,
                ScheduledTime = scheduledTimeUtc,
            };

            if (operationInput != null)
            {
                request.SetInput(operationInput, this.messageDataConverter);
            }

            var jrequest  = JToken.FromObject(request, this.messageDataConverter.JsonSerializer);
            var eventName = scheduledTimeUtc.HasValue ? EntityMessageEventNames.ScheduledRequestMessageEventName(scheduledTimeUtc.Value) : EntityMessageEventNames.RequestMessageEventName;
            await client.RaiseEventAsync(instance, eventName, jrequest);

            this.traceHelper.FunctionScheduled(
                hubName,
                entityId.EntityName,
                EntityId.GetSchedulerIdFromEntityId(entityId),
                reason: $"EntitySignal:{operationName}",
                functionType: FunctionType.Entity,
                isReplay: false);
        }
Exemplo n.º 6
0
        internal void SendResponseMessage(OrchestrationInstance target, Guid requestId, object message, bool isException)
        {
            lock (this.outbox)
            {
                if (message is RequestMessage requestMessage)
                {
                    this.State.MessageSorter.LabelOutgoingMessage(requestMessage, target.InstanceId, DateTime.UtcNow, this.EntityMessageReorderWindow);
                }

                this.outbox.Add(new ResultMessage()
                {
                    Target       = target,
                    EventName    = EntityMessageEventNames.ResponseMessageEventName(requestId),
                    EventContent = message,
                    IsError      = isException,
                });
            }
        }
Exemplo n.º 7
0
        internal void SendEntityMessage(OrchestrationInstance target, object eventContent)
        {
            string eventName;

            if (eventContent is RequestMessage requestMessage)
            {
                if (requestMessage.ScheduledTime.HasValue)
                {
                    eventName = EntityMessageEventNames.ScheduledRequestMessageEventName(requestMessage.ScheduledTime.Value);
                }
                else
                {
                    this.MessageSorter.LabelOutgoingMessage(
                        requestMessage,
                        target.InstanceId,
                        this.InnerContext.CurrentUtcDateTime,
                        TimeSpan.FromMinutes(this.Config.Options.EntityMessageReorderWindowInMinutes));

                    eventName = EntityMessageEventNames.RequestMessageEventName;
                }
            }
            else
            {
                eventName = EntityMessageEventNames.ReleaseMessageEventName;
            }

            if (!this.IsReplaying)
            {
                this.Config.TraceHelper.SendingEntityMessage(
                    this.InstanceId,
                    this.ExecutionId,
                    target.InstanceId,
                    eventName,
                    eventContent);
            }

            this.IncrementActionsOrThrowException();
            this.InnerContext.SendEvent(target, eventName, eventContent);
        }
Exemplo n.º 8
0
        internal void SendOperationMessage(OrchestrationInstance target, RequestMessage requestMessage)
        {
            lock (this.outbox)
            {
                string eventName;

                if (requestMessage.ScheduledTime.HasValue)
                {
                    eventName = EntityMessageEventNames.ScheduledRequestMessageEventName(requestMessage.ScheduledTime.Value);
                }
                else
                {
                    eventName = EntityMessageEventNames.RequestMessageEventName;
                }

                this.outbox.Add(new OperationMessage()
                {
                    Target       = target,
                    EventName    = eventName,
                    EventContent = requestMessage,
                });
            }
        }
Exemplo n.º 9
0
        internal void SendOperationMessage(OrchestrationInstance target, RequestMessage requestMessage)
        {
            lock (this.outbox)
            {
                string eventName;

                if (requestMessage.ScheduledTime.HasValue)
                {
                    DateTime adjustedDeliveryTime = requestMessage.GetAdjustedDeliveryTime(this.durabilityProvider);
                    eventName = EntityMessageEventNames.ScheduledRequestMessageEventName(adjustedDeliveryTime);
                }
                else
                {
                    eventName = EntityMessageEventNames.RequestMessageEventName;
                }

                this.outbox.Add(new OperationMessage()
                {
                    Target       = target,
                    EventName    = eventName,
                    EventContent = requestMessage,
                });
            }
        }
Exemplo n.º 10
0
        internal void SendOperationMessage(OrchestrationInstance target, RequestMessage requestMessage)
        {
            lock (this.outbox)
            {
                string eventName;

                if (requestMessage.ScheduledTime.HasValue)
                {
                    eventName = EntityMessageEventNames.ScheduledRequestMessageEventName(requestMessage.ScheduledTime.Value);
                }
                else
                {
                    this.State.MessageSorter.LabelOutgoingMessage(requestMessage, target.InstanceId, DateTime.UtcNow, this.EntityMessageReorderWindow);
                    eventName = EntityMessageEventNames.RequestMessageEventName;
                }

                this.outbox.Add(new OperationMessage()
                {
                    Target       = target,
                    EventName    = eventName,
                    EventContent = requestMessage,
                });
            }
        }
Exemplo n.º 11
0
        private async Task EntityMiddleware(DispatchMiddlewareContext dispatchContext, Func <Task> next)
        {
            var entityShim = dispatchContext.GetProperty <TaskOrchestration>() as TaskEntityShim;

            if (entityShim == null)
            {
                // This is not an entity - skip.
                await next();

                return;
            }

            OrchestrationRuntimeState runtimeState  = dispatchContext.GetProperty <OrchestrationRuntimeState>();
            DurableEntityContext      entityContext = (DurableEntityContext)entityShim.Context;

            entityContext.InstanceId  = runtimeState.OrchestrationInstance.InstanceId;
            entityContext.ExecutionId = runtimeState.OrchestrationInstance.ExecutionId;
            entityContext.History     = runtimeState.Events;
            entityContext.RawInput    = runtimeState.Input;

            try
            {
                // 1. First time through the history
                // we count events, add any under-lock op to the batch, and process lock releases
                foreach (HistoryEvent e in runtimeState.Events)
                {
                    switch (e.EventType)
                    {
                    case EventType.ExecutionStarted:
                        entityShim.Rehydrate(runtimeState.Input);
                        break;

                    case EventType.EventRaised:
                        EventRaisedEvent eventRaisedEvent = (EventRaisedEvent)e;

                        this.TraceHelper.DeliveringEntityMessage(
                            entityContext.InstanceId,
                            entityContext.ExecutionId,
                            e.EventId,
                            eventRaisedEvent.Name,
                            eventRaisedEvent.Input);

                        entityShim.NumberEventsToReceive++;

                        if (EntityMessageEventNames.IsRequestMessage(eventRaisedEvent.Name))
                        {
                            // we are receiving an operation request or a lock request
                            var requestMessage = this.DataConverter.Deserialize <RequestMessage>(eventRaisedEvent.Input);

                            IEnumerable <RequestMessage> deliverNow;

                            if (requestMessage.ScheduledTime.HasValue)
                            {
                                // messages with a scheduled time are always delivered immediately
                                deliverNow = new RequestMessage[] { requestMessage };
                            }
                            else
                            {
                                // run this through the message sorter to help with reordering and duplicate filtering
                                deliverNow = entityContext.State.MessageSorter.ReceiveInOrder(requestMessage, entityContext.EntityMessageReorderWindow);
                            }

                            foreach (var message in deliverNow)
                            {
                                if (entityContext.State.LockedBy == message.ParentInstanceId)
                                {
                                    // operation requests from the lock holder are processed immediately
                                    entityShim.AddOperationToBatch(message);
                                }
                                else
                                {
                                    // others go to the back of the queue
                                    entityContext.State.Enqueue(message);
                                }
                            }
                        }
                        else
                        {
                            // we are receiving a lock release
                            var message = this.DataConverter.Deserialize <ReleaseMessage>(eventRaisedEvent.Input);

                            if (entityContext.State.LockedBy == message.ParentInstanceId)
                            {
                                this.TraceHelper.EntityLockReleased(
                                    entityContext.HubName,
                                    entityContext.Name,
                                    entityContext.InstanceId,
                                    message.ParentInstanceId,
                                    message.LockRequestId,
                                    isReplay: false);

                                entityContext.State.LockedBy = null;
                            }
                        }

                        break;
                    }
                }

                // 2. We add as many requests from the queue to the batch as possible (stopping at lock requests)
                while (entityContext.State.LockedBy == null &&
                       entityContext.State.TryDequeue(out var request))
                {
                    if (request.IsLockRequest)
                    {
                        entityShim.AddLockRequestToBatch(request);
                        entityContext.State.LockedBy = request.ParentInstanceId;
                    }
                    else
                    {
                        entityShim.AddOperationToBatch(request);
                    }
                }
            }
            catch (Exception e)
            {
                entityContext.CaptureInternalError(e);
            }

            // 3. Start the functions invocation pipeline (billing, logging, bindings, and timeout tracking).
            FunctionResult result = await entityShim.GetFunctionInfo().Executor.TryExecuteAsync(
                new TriggeredFunctionData
            {
                TriggerValue = entityShim.Context,

#pragma warning disable CS0618 // Approved for use by this extension
                InvokeHandler = async userCodeInvoker =>
                {
                    entityShim.SetFunctionInvocationCallback(userCodeInvoker);

                    // 3. Run all the operations in the batch
                    if (entityContext.InternalError == null)
                    {
                        try
                        {
                            await entityShim.ExecuteBatch();
                        }
                        catch (Exception e)
                        {
                            entityContext.CaptureInternalError(e);
                        }
                    }

                    // 4. Run the DTFx orchestration to persist the effects,
                    // send the outbox, and continue as new
                    await next();

                    // 5. If there were internal or application errors, indicate to the functions host
                    entityContext.ThrowInternalExceptionIfAny();
                    entityContext.ThrowApplicationExceptionsIfAny();
                },
#pragma warning restore CS0618
            },
                CancellationToken.None);

            await entityContext.RunDeferredTasks();

            // If there were internal errors, do not commit the batch, but instead rethrow
            // here so DTFx can abort the batch and back off the work item
            entityContext.ThrowInternalExceptionIfAny();
        }