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); }
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); }
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); }
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); }