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,
            };
        }
Example #2
0
        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}");
            }
        }