internal TaskActivityDispatcher( IOrchestrationService orchestrationService, INameVersionObjectManager <TaskActivity> objectManager, DispatchMiddlewarePipeline dispatchPipeline, LogHelper logHelper, ErrorPropagationMode errorPropagationMode) { this.orchestrationService = orchestrationService ?? throw new ArgumentNullException(nameof(orchestrationService)); this.objectManager = objectManager ?? throw new ArgumentNullException(nameof(objectManager)); this.dispatchPipeline = dispatchPipeline ?? throw new ArgumentNullException(nameof(dispatchPipeline)); this.logHelper = logHelper; this.errorPropagationMode = errorPropagationMode; this.dispatcher = new WorkItemDispatcher <TaskActivityWorkItem>( "TaskActivityDispatcher", item => item.Id, this.OnFetchWorkItemAsync, this.OnProcessWorkItemAsync) { AbortWorkItem = orchestrationService.AbandonTaskActivityWorkItemAsync, GetDelayInSecondsAfterOnFetchException = orchestrationService.GetDelayInSecondsAfterOnFetchException, GetDelayInSecondsAfterOnProcessException = orchestrationService.GetDelayInSecondsAfterOnProcessException, DispatcherCount = orchestrationService.TaskActivityDispatcherCount, MaxConcurrentWorkItems = orchestrationService.MaxConcurrentTaskActivityWorkItems, LogHelper = logHelper, }; }
internal TaskOrchestrationDispatcher( IOrchestrationService orchestrationService, INameVersionObjectManager <TaskOrchestration> objectManager, DispatchMiddlewarePipeline dispatchPipeline, LogHelper logHelper, ErrorPropagationMode errorPropagationMode) { this.objectManager = objectManager ?? throw new ArgumentNullException(nameof(objectManager)); this.orchestrationService = orchestrationService ?? throw new ArgumentNullException(nameof(orchestrationService)); this.dispatchPipeline = dispatchPipeline ?? throw new ArgumentNullException(nameof(dispatchPipeline)); this.logHelper = logHelper ?? throw new ArgumentNullException(nameof(logHelper)); this.errorPropagationMode = errorPropagationMode; this.dispatcher = new WorkItemDispatcher <TaskOrchestrationWorkItem>( "TaskOrchestrationDispatcher", item => item == null ? string.Empty : item.InstanceId, this.OnFetchWorkItemAsync, this.OnProcessWorkItemSessionAsync) { GetDelayInSecondsAfterOnFetchException = orchestrationService.GetDelayInSecondsAfterOnFetchException, GetDelayInSecondsAfterOnProcessException = orchestrationService.GetDelayInSecondsAfterOnProcessException, SafeReleaseWorkItem = orchestrationService.ReleaseTaskOrchestrationWorkItemAsync, AbortWorkItem = orchestrationService.AbandonTaskOrchestrationWorkItemAsync, DispatcherCount = orchestrationService.TaskOrchestrationDispatcherCount, MaxConcurrentWorkItems = orchestrationService.MaxConcurrentTaskOrchestrationWorkItems, LogHelper = logHelper, }; // To avoid starvation, we only allow half of all concurrently execution orchestrations to // leverage extended sessions. var maxConcurrentSessions = (int)Math.Ceiling(this.dispatcher.MaxConcurrentWorkItems / 2.0); this.concurrentSessionLock = new NonBlockingCountdownLock(maxConcurrentSessions); }
public async Task CatchInvalidOperationException(ErrorPropagationMode mode) { // The error propagation mode must be set before the worker is started this.worker.ErrorPropagationMode = mode; await this.worker .AddTaskOrchestrations(typeof(ExceptionHandlingOrchestration)) .AddTaskActivities(typeof(ThrowInvalidOperationException)) .StartAsync(); // This is required for exceptions to be serialized this.worker.TaskActivityDispatcher.IncludeDetails = true; OrchestrationInstance instance = await this.client.CreateOrchestrationInstanceAsync(typeof(ExceptionHandlingOrchestration), null); OrchestrationState state = await this.client.WaitForOrchestrationAsync(instance, DefaultTimeout); Assert.IsNotNull(state); Assert.AreEqual(OrchestrationStatus.Completed, state.OrchestrationStatus); Assert.IsNotNull(state.Output, "The expected error information wasn't found!"); if (mode == ErrorPropagationMode.SerializeExceptions) { // The exception should be deserializable InvalidOperationException e = JsonConvert.DeserializeObject <InvalidOperationException>(state.Output); Assert.IsNotNull(e); Assert.AreEqual("This is a test exception", e.Message); } else if (mode == ErrorPropagationMode.UseFailureDetails) { // The failure details should contain the relevant exception metadata FailureDetails details = JsonConvert.DeserializeObject <FailureDetails>(state.Output); Assert.IsNotNull(details); Assert.AreEqual(typeof(InvalidOperationException).FullName, details.ErrorType); Assert.IsTrue(details.IsCausedBy <InvalidOperationException>()); Assert.IsTrue(details.IsCausedBy <Exception>()); // check that base types work too Assert.AreEqual("This is a test exception", details.ErrorMessage); Assert.IsNotNull(details.StackTrace); // The callstack should be in the error details string expectedCallstackSubstring = typeof(ThrowInvalidOperationException).FullName !.Replace('+', '.'); Assert.IsTrue( details.StackTrace !.IndexOf(expectedCallstackSubstring) > 0, $"Expected to find {expectedCallstackSubstring} in the exception details. Actual: {details.StackTrace}"); } else { Assert.Fail($"Unexpected {nameof(ErrorPropagationMode)} value: {mode}"); } }
public TaskOrchestrationExecutor( OrchestrationRuntimeState orchestrationRuntimeState, TaskOrchestration taskOrchestration, BehaviorOnContinueAsNew eventBehaviourForContinueAsNew, ErrorPropagationMode errorPropagationMode = ErrorPropagationMode.SerializeExceptions) { this.decisionScheduler = new SynchronousTaskScheduler(); this.context = new TaskOrchestrationContext( orchestrationRuntimeState.OrchestrationInstance, this.decisionScheduler, errorPropagationMode); this.orchestrationRuntimeState = orchestrationRuntimeState; this.taskOrchestration = taskOrchestration; this.skipCarryOverEvents = eventBehaviourForContinueAsNew == BehaviorOnContinueAsNew.Ignore; }
public TaskOrchestrationContext( OrchestrationInstance orchestrationInstance, TaskScheduler taskScheduler, ErrorPropagationMode errorPropagationMode = ErrorPropagationMode.SerializeExceptions) { Utils.UnusedParameter(taskScheduler); this.openTasks = new Dictionary <int, OpenTaskInfo>(); this.orchestratorActionsMap = new SortedDictionary <int, OrchestratorAction>(); this.idCounter = 0; this.MessageDataConverter = JsonDataConverter.Default; this.ErrorDataConverter = JsonDataConverter.Default; OrchestrationInstance = orchestrationInstance; IsReplaying = false; ErrorPropagationMode = errorPropagationMode; }
public async Task FailureDetailsOnUnhandled(ErrorPropagationMode mode) { // The error propagation mode must be set before the worker is started this.worker.ErrorPropagationMode = mode; await this.worker .AddTaskOrchestrations(typeof(NoExceptionHandlingOrchestration)) .AddTaskActivities(typeof(ThrowInvalidOperationException)) .StartAsync(); OrchestrationInstance instance = await this.client.CreateOrchestrationInstanceAsync( typeof(NoExceptionHandlingOrchestration), input : null); OrchestrationState state = await this.client.WaitForOrchestrationAsync(instance, DefaultTimeout); Assert.IsNotNull(state); Assert.AreEqual(OrchestrationStatus.Failed, state.OrchestrationStatus); string expectedErrorMessage = "This is a test exception"; if (mode == ErrorPropagationMode.SerializeExceptions) { // Legacy behavior is to set the output of the orchestration to be the exception message Assert.AreEqual(expectedErrorMessage, state.Output); } else if (mode == ErrorPropagationMode.UseFailureDetails) { string activityName = typeof(ThrowInvalidOperationException).FullName !; string expectedOutput = $"{typeof(TaskFailedException).FullName}: Task '{activityName}' (#0) failed with an unhandled exception: {expectedErrorMessage}"; Assert.AreEqual(expectedOutput, state.Output); } else { Assert.Fail($"Unexpected {nameof(ErrorPropagationMode)} value: {mode}"); } }