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);
        }
        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);
        }
示例#3
0
        private void SignalEntityInternal(EntityId entity, DateTime?scheduledTimeUtc, string operation, object input)
        {
            this.ThrowIfInvalidAccess();
            if (operation == null)
            {
                throw new ArgumentNullException(nameof(operation));
            }

            string functionName = entity.EntityName;

            this.Config.ThrowIfFunctionDoesNotExist(functionName, FunctionType.Entity);

            var target = new OrchestrationInstance()
            {
                InstanceId = EntityId.GetSchedulerIdFromEntityId(entity),
            };
            var request = new RequestMessage()
            {
                ParentInstanceId  = this.InstanceId,
                ParentExecutionId = null, // for entities, message sorter persists across executions
                Id            = Guid.NewGuid(),
                IsSignal      = true,
                Operation     = operation,
                ScheduledTime = scheduledTimeUtc,
            };

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

            this.SendOperationMessage(target, request);

            this.Config.TraceHelper.FunctionScheduled(
                this.Config.Options.HubName,
                functionName,
                this.InstanceId,
                reason: this.FunctionName,
                functionType: FunctionType.Entity,
                isReplay: false);
        }
示例#4
0
        void IDurableEntityContext.SignalEntity(EntityId entity, string operation, object input)
        {
            this.ThrowIfInvalidAccess();
            if (operation == null)
            {
                throw new ArgumentNullException(nameof(operation));
            }

            string functionName = entity.EntityName;

            this.Config.ThrowIfFunctionDoesNotExist(functionName, FunctionType.Entity);

            var target = new OrchestrationInstance()
            {
                InstanceId = EntityId.GetSchedulerIdFromEntityId(entity),
            };
            var request = new RequestMessage()
            {
                ParentInstanceId = this.InstanceId,
                Id        = Guid.NewGuid(),
                IsSignal  = true,
                Operation = operation,
            };

            if (input != null)
            {
                request.SetInput(input);
            }

            this.SendOperationMessage(target, "op", request);

            this.Config.TraceHelper.FunctionScheduled(
                this.Config.Options.HubName,
                functionName,
                this.InstanceId,
                reason: this.FunctionName,
                functionType: FunctionType.Entity,
                isReplay: false);
        }
        private async Task SignalEntityAsync(TaskHubClient client, string hubName, EntityId entityId, string operationName, object operationInput)
        {
            if (operationName == null)
            {
                throw new ArgumentNullException(nameof(operationName));
            }

            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,
                Id        = guid,
                IsSignal  = true,
                Operation = operationName,
            };

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

            var jrequest = JToken.FromObject(request, MessagePayloadDataConverter.DefaultSerializer);
            await client.RaiseEventAsync(instance, "op", jrequest);

            this.traceHelper.FunctionScheduled(
                hubName,
                entityId.EntityName,
                EntityId.GetSchedulerIdFromEntityId(entityId),
                reason: $"EntitySignal:{operationName}",
                functionType: FunctionType.Entity,
                isReplay: false);
        }
示例#6
0
        internal async Task <TResult> CallDurableTaskFunctionAsync <TResult>(
            string functionName,
            FunctionType functionType,
            bool oneWay,
            string instanceId,
            string operation,
            RetryOptions retryOptions,
            object input)
        {
            this.ThrowIfInvalidAccess();

            if (retryOptions != null)
            {
                if (!this.durabilityProvider.ValidateDelayTime(retryOptions.MaxRetryInterval, out string errorMessage))
                {
                    throw new ArgumentException(errorMessage, nameof(retryOptions.MaxRetryInterval));
                }

                if (!this.durabilityProvider.ValidateDelayTime(retryOptions.FirstRetryInterval, out errorMessage))
                {
                    throw new ArgumentException(errorMessage, nameof(retryOptions.FirstRetryInterval));
                }
            }

            // TODO: Support for versioning
            string version = DefaultVersion;

            this.Config.ThrowIfFunctionDoesNotExist(functionName, functionType);

            Task <TResult> callTask      = null;
            EntityId?      lockToUse     = null;
            string         operationId   = string.Empty;
            string         operationName = string.Empty;

            switch (functionType)
            {
            case FunctionType.Activity:
                System.Diagnostics.Debug.Assert(instanceId == null, "The instanceId parameter should not be used for activity functions.");
                System.Diagnostics.Debug.Assert(operation == null, "The operation parameter should not be used for activity functions.");
                System.Diagnostics.Debug.Assert(!oneWay, "The oneWay parameter should not be used for activity functions.");
                if (retryOptions == null)
                {
                    this.IncrementActionsOrThrowException();
                    callTask = this.InnerContext.ScheduleTask <TResult>(functionName, version, input);
                }
                else
                {
                    this.IncrementActionsOrThrowException();
                    callTask = this.InnerContext.ScheduleWithRetry <TResult>(
                        functionName,
                        version,
                        retryOptions.GetRetryOptions(),
                        input);
                }

                break;

            case FunctionType.Orchestrator:
                // Instance IDs should not be reused when creating sub-orchestrations. This is a best-effort
                // check. We cannot easily check the full hierarchy, so we just look at the current orchestration
                // and the immediate parent.
                if (string.Equals(instanceId, this.InstanceId, StringComparison.OrdinalIgnoreCase) ||
                    (this.ParentInstanceId != null && string.Equals(instanceId, this.ParentInstanceId, StringComparison.OrdinalIgnoreCase)))
                {
                    throw new ArgumentException("The instance ID of a sub-orchestration must be different than the instance ID of a parent orchestration.");
                }

                System.Diagnostics.Debug.Assert(operation == null, "The operation parameter should not be used for activity functions.");
                if (instanceId != null && instanceId.StartsWith("@"))
                {
                    throw new ArgumentException(nameof(instanceId), "Orchestration instance ids must not start with @");
                }

                if (oneWay)
                {
                    this.IncrementActionsOrThrowException();
                    var dummyTask = this.InnerContext.CreateSubOrchestrationInstance <TResult>(
                        functionName,
                        version,
                        instanceId,
                        input,
                        new Dictionary <string, string>()
                    {
                        { OrchestrationTags.FireAndForget, "" }
                    });

                    System.Diagnostics.Debug.Assert(dummyTask.IsCompleted, "task should be fire-and-forget");
                }
                else
                {
                    if (this.ContextLocks != null)
                    {
                        throw new LockingRulesViolationException("While holding locks, cannot call suborchestrators.");
                    }

                    if (retryOptions == null)
                    {
                        this.IncrementActionsOrThrowException();
                        callTask = this.InnerContext.CreateSubOrchestrationInstance <TResult>(
                            functionName,
                            version,
                            instanceId,
                            input);
                    }
                    else
                    {
                        this.IncrementActionsOrThrowException();
                        callTask = this.InnerContext.CreateSubOrchestrationInstanceWithRetry <TResult>(
                            functionName,
                            version,
                            instanceId,
                            retryOptions.GetRetryOptions(),
                            input);
                    }
                }

                break;

            case FunctionType.Entity:
                System.Diagnostics.Debug.Assert(operation != null, "The operation parameter is required.");
                System.Diagnostics.Debug.Assert(retryOptions == null, "Retries are not supported for entity calls.");
                System.Diagnostics.Debug.Assert(instanceId != null, "Entity calls need to specify the target entity.");

                if (this.ContextLocks != null)
                {
                    lockToUse = EntityId.GetEntityIdFromSchedulerId(instanceId);
                    if (oneWay)
                    {
                        if (this.ContextLocks.Contains(lockToUse.Value))
                        {
                            throw new LockingRulesViolationException("While holding locks, cannot signal entities whose lock is held.");
                        }
                    }
                    else
                    {
                        if (!this.ContextLocks.Remove(lockToUse.Value))
                        {
                            throw new LockingRulesViolationException("While holding locks, cannot call entities whose lock is not held.");
                        }
                    }
                }

                var guid   = this.NewGuid();   // deterministically replayable unique id for this request
                var target = new OrchestrationInstance()
                {
                    InstanceId = instanceId
                };
                operationId   = guid.ToString();
                operationName = operation;
                var request = new RequestMessage()
                {
                    ParentInstanceId = this.InstanceId,
                    Id        = guid,
                    IsSignal  = oneWay,
                    Operation = operation,
                };
                if (input != null)
                {
                    request.SetInput(input);
                }

                this.SendEntityMessage(target, "op", request);

                if (!oneWay)
                {
                    callTask = this.WaitForEntityResponse <TResult>(guid, lockToUse);
                }

                break;

            default:
                throw new InvalidOperationException($"Unexpected function type '{functionType}'.");
            }

            string sourceFunctionId = this.FunctionName;

            this.Config.TraceHelper.FunctionScheduled(
                this.Config.Options.HubName,
                functionName,
                this.InstanceId,
                reason: sourceFunctionId,
                functionType: functionType,
                isReplay: this.IsReplaying);

            TResult   output;
            Exception exception = null;

            if (oneWay)
            {
                return(default(TResult));
            }

            System.Diagnostics.Debug.Assert(callTask != null, "Two-way operations are asynchronous, so callTask must not be null.");

            try
            {
                output = await callTask;
            }
            catch (TaskFailedException e)
            {
                exception = e;
                string message = string.Format(
                    "The {0} function '{1}' failed: \"{2}\". See the function execution logs for additional details.",
                    functionType.ToString().ToLowerInvariant(),
                    functionName,
                    e.InnerException?.Message);
                throw new FunctionFailedException(message, e.InnerException);
            }
            catch (SubOrchestrationFailedException e)
            {
                exception = e;
                string message = string.Format(
                    "The {0} function '{1}' failed: \"{2}\". See the function execution logs for additional details.",
                    functionType.ToString().ToLowerInvariant(),
                    functionName,
                    e.InnerException?.Message);
                throw new FunctionFailedException(message, e.InnerException);
            }
            catch (Exception e)
            {
                exception = e;
                throw;
            }
            finally
            {
                if (exception != null && this.IsReplaying)
                {
                    // If this were not a replay, then the orchestrator/activity/entity function trigger would have already
                    // emitted a FunctionFailed trace with the full exception details.

                    if (functionType == FunctionType.Entity)
                    {
                        this.Config.TraceHelper.OperationFailed(
                            this.Config.Options.HubName,
                            functionName,
                            this.InstanceId,
                            operationId,
                            operationName,
                            input: "(replayed)",
                            exception: "(replayed)",
                            duration: 0,
                            isReplay: true);
                    }
                    else
                    {
                        this.Config.TraceHelper.FunctionFailed(
                            this.Config.Options.HubName,
                            functionName,
                            this.InstanceId,
                            reason: $"(replayed {exception.GetType().Name})",
                            functionType: functionType,
                            isReplay: true);
                    }
                }
            }

            if (this.IsReplaying)
            {
                // If this were not a replay, then the orchestrator/activity/entity function trigger would have already
                // emitted a FunctionCompleted trace with the actual output details.

                if (functionType == FunctionType.Entity)
                {
                    this.Config.TraceHelper.OperationCompleted(
                        this.Config.Options.HubName,
                        functionName,
                        this.InstanceId,
                        operationId,
                        operationName,
                        input: "(replayed)",
                        output: "(replayed)",
                        duration: 0,
                        isReplay: true);
                }
                else
                {
                    this.Config.TraceHelper.FunctionCompleted(
                        this.Config.Options.HubName,
                        functionName,
                        this.InstanceId,
                        output: "(replayed)",
                        continuedAsNew: false,
                        functionType: functionType,
                        isReplay: true);
                }
            }

            return(output);
        }