private void HandleExceptionAsync(ProcessingCommand processingCommand, ICommandHandlerProxy commandHandler, Exception exception, int retryTimes)
        {
            var command = processingCommand.Message;

            _ioHelper.TryAsyncActionRecursively <AsyncTaskResult <HandledCommand> >("GetCommandAsync",
                                                                                    () => _commandStore.GetAsync(command.Id),
                                                                                    currentRetryTimes => HandleExceptionAsync(processingCommand, commandHandler, exception, currentRetryTimes),
                                                                                    result =>
            {
                var existingHandledCommand = result.Data;
                if (existingHandledCommand != null)
                {
                    HandleExistingHandledCommandForExceptionAsync(processingCommand, existingHandledCommand, commandHandler, exception, 0);
                }
                else
                {
                    //到这里,说明当前command执行遇到异常,然后当前command之前也没执行过,是第一次被执行。
                    //那就判断当前异常是否是需要被发布出去的异常,如果是,则发布该异常给所有消费者;否则,就记录错误日志;
                    //然后,认为该command处理失败即可;
                    var publishableException = exception as IPublishableException;
                    if (publishableException != null)
                    {
                        PublishExceptionAsync(processingCommand, publishableException, 0);
                    }
                    else
                    {
                        LogCommandExecuteException(processingCommand, commandHandler, exception);
                        NotifyCommandExecuted(processingCommand, CommandStatus.Failed, exception.GetType().Name, exception.Message);
                    }
                }
            },
                                                                                    () => string.Format("[commandId:{0}]", command.Id),
                                                                                    () => NotifyCommandExecuted(processingCommand, CommandStatus.Failed, null, "Get command async failed."),
                                                                                    retryTimes);
        }
        private Task HandleCommandInternal(ProcessingCommand processingCommand, ICommandHandlerProxy commandHandler, int retryTimes, TaskCompletionSource <bool> taskSource)
        {
            var command        = processingCommand.Message;
            var commandContext = processingCommand.CommandExecuteContext;

            commandContext.Clear();

            _ioHelper.TryAsyncActionRecursivelyWithoutResult("HandleCommandAsync",
                                                             async() =>
            {
                await commandHandler.HandleAsync(commandContext, command).ConfigureAwait(false);
            },
                                                             currentRetryTimes => HandleCommandInternal(processingCommand, commandHandler, currentRetryTimes, taskSource),
                                                             async() =>
            {
                if (_logger.IsDebugEnabled)
                {
                    _logger.DebugFormat("Handle command success. handlerType:{0}, commandType:{1}, commandId:{2}, aggregateRootId:{3}",
                                        commandHandler.GetInnerObject().GetType().Name,
                                        command.GetType().Name,
                                        command.Id,
                                        command.AggregateRootId);
                }
                if (commandContext.GetApplicationMessage() != null)
                {
                    await CommitChangesAsync(processingCommand, true, commandContext.GetApplicationMessage(), null, new TaskCompletionSource <bool>()).ConfigureAwait(false);
                    taskSource.SetResult(true);
                }
                else
                {
                    try
                    {
                        await CommitAggregateChanges(processingCommand).ConfigureAwait(false);
                        taskSource.SetResult(true);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error(string.Format("Commit aggregate changes has unknown exception, handlerType:{0}, commandType:{1}, commandId:{2}, aggregateRootId:{3}",
                                                    commandHandler.GetInnerObject().GetType().Name,
                                                    command.GetType().Name,
                                                    command.Id,
                                                    command.AggregateRootId), ex);
                        await CompleteCommand(processingCommand, CommandStatus.Failed, ex.GetType().Name, "Unknown exception caught when committing changes of command.").ConfigureAwait(false);
                        taskSource.SetResult(true);
                    }
                }
            },
                                                             () => string.Format("[command:[id:{0},type:{1}],handlerType:{2},aggregateRootId:{3}]", command.Id, command.GetType().Name, commandHandler.GetInnerObject().GetType().Name, command.AggregateRootId),
                                                             async(ex, errorMessage) =>
            {
                await HandleExceptionAsync(processingCommand, commandHandler, ex, errorMessage, 0, new TaskCompletionSource <bool>()).ConfigureAwait(false);
                taskSource.SetResult(true);
            },
                                                             retryTimes);

            return(taskSource.Task);
        }
        private void LogCommandExecuteException(ProcessingCommand processingCommand, ICommandHandlerProxy commandHandler, Exception exception)
        {
            var errorMessage = string.Format("{0} raised when {1} handling {2}. commandId:{3}, aggregateRootId:{4}",
                                             exception.GetType().Name,
                                             commandHandler.GetInnerHandler().GetType().Name,
                                             processingCommand.Message.GetType().Name,
                                             processingCommand.Message.Id,
                                             processingCommand.Message.AggregateRootId);

            _logger.Error(errorMessage, exception);
        }
        private void HandleCommand(ProcessingCommand processingCommand, ICommandHandlerProxy commandHandler)
        {
            var command = processingCommand.Message;

            //调用command handler执行当前command
            var handleSuccess = false;

            try
            {
                commandHandler.Handle(processingCommand.CommandExecuteContext, command);
                if (_logger.IsDebugEnabled)
                {
                    _logger.DebugFormat("Handle command success. handlerType:{0}, commandType:{1}, commandId:{2}, aggregateRootId:{3}",
                                        commandHandler.GetInnerHandler().GetType().Name,
                                        command.GetType().Name,
                                        command.Id,
                                        processingCommand.Message.AggregateRootId);
                }
                handleSuccess = true;
            }
            catch (IOException ex)
            {
                _logger.Error(ex);
                RetryCommand(processingCommand);
                return;
            }
            catch (Exception ex)
            {
                HandleExceptionAsync(processingCommand, commandHandler, ex, 0);
                return;
            }

            //如果command执行成功,则提交执行后的结果
            if (handleSuccess)
            {
                try
                {
                    CommitAggregateChanges(processingCommand);
                }
                catch (Exception ex)
                {
                    LogCommandExecuteException(processingCommand, commandHandler, ex);
                    NotifyCommandExecuted(processingCommand, CommandStatus.Failed, ex.GetType().Name, "Unknown exception caught when committing changes of command.");
                }
            }
        }
        private void HandleExceptionAsync(ProcessingCommand processingCommand, ICommandHandlerProxy commandHandler, Exception exception, int retryTimes)
        {
            var command = processingCommand.Message;

            _ioHelper.TryAsyncActionRecursively("FindEventByCommandIdAsync",
                                                () => _eventStore.FindAsync(command.AggregateRootId, command.Id),
                                                currentRetryTimes => HandleExceptionAsync(processingCommand, commandHandler, exception, currentRetryTimes),
                                                result =>
            {
                var existingEventStream = result.Data;
                if (existingEventStream != null)
                {
                    //这里,我们需要再重新做一遍发布事件这个操作;
                    //之所以要这样做是因为虽然该command产生的事件已经持久化成功,但并不表示事件已经发布出去了;
                    //因为有可能事件持久化成功了,但那时正好机器断电了,则发布事件就没有做;
                    _eventService.PublishDomainEventAsync(processingCommand, existingEventStream);
                    processingCommand.Mailbox.TryExecuteNextMessage();
                }
                else
                {
                    //到这里,说明当前command执行遇到异常,然后当前command之前也没执行过,是第一次被执行。
                    //那就判断当前异常是否是需要被发布出去的异常,如果是,则发布该异常给所有消费者;
                    //否则,就记录错误日志,然后认为该command处理失败即可;
                    var publishableException = exception as IPublishableException;
                    if (publishableException != null)
                    {
                        PublishExceptionAsync(processingCommand, publishableException, 0);
                    }
                    else
                    {
                        LogCommandExecuteException(processingCommand, commandHandler, exception);
                        CompleteCommand(processingCommand, CommandStatus.Failed, exception.GetType().Name, exception.Message);
                    }
                }
            },
                                                () => string.Format("[commandId:{0}]", command.Id),
                                                errorMessage =>
            {
                _logger.Fatal(string.Format("Find event by commandId has unknown exception, the code should not be run to here, errorMessage: {0}", errorMessage));
            },
                                                retryTimes, true);
        }
        private Task HandleExceptionAsync(ProcessingCommand processingCommand, ICommandHandlerProxy commandHandler, Exception exception, string errorMessage, int retryTimes, TaskCompletionSource <bool> taskSource)
        {
            var command = processingCommand.Message;

            _ioHelper.TryAsyncActionRecursively("FindEventByCommandIdAsync",
                                                () => _eventStore.FindAsync(command.AggregateRootId, command.Id),
                                                currentRetryTimes => HandleExceptionAsync(processingCommand, commandHandler, exception, errorMessage, currentRetryTimes, taskSource),
                                                async result =>
            {
                var existingEventStream = result;
                if (existingEventStream != null)
                {
                    //这里,我们需要再重新做一遍发布事件这个操作;
                    //之所以要这样做是因为虽然该command产生的事件已经持久化成功,但并不表示事件已经发布出去了;
                    //因为有可能事件持久化成功了,但那时正好机器断电了,则发布事件就没有做;
                    _eventCommittingService.PublishDomainEventAsync(processingCommand, existingEventStream);
                    taskSource.SetResult(true);
                }
                else
                {
                    //到这里,说明当前command执行遇到异常,然后当前command之前也没执行过,是第一次被执行。
                    //那就判断当前异常是否是需要被发布出去的异常,如果是,则发布该异常给所有消费者;
                    //否则,就记录错误日志,然后认为该command处理失败即可;
                    var domainException = TryGetDomainException(exception);
                    if (domainException != null)
                    {
                        await PublishExceptionAsync(processingCommand, domainException, 0, new TaskCompletionSource <bool>()).ConfigureAwait(false);
                        taskSource.SetResult(true);
                    }
                    else
                    {
                        await CompleteCommand(processingCommand, CommandStatus.Failed, exception.GetType().Name, exception != null ? exception.Message : errorMessage).ConfigureAwait(false);
                        taskSource.SetResult(true);
                    }
                }
            },
                                                () => string.Format("[command:[id:{0},type:{1}],handlerType:{2},aggregateRootId:{3}]", command.Id, command.GetType().Name, commandHandler.GetInnerObject().GetType().Name, command.AggregateRootId),
                                                null,
                                                retryTimes, true);

            return(taskSource.Task);
        }
Example #7
0
        private void HandleExceptionAsync(ProcessingCommand processingCommand, ICommandHandlerProxy commandHandler, Exception exception, int retryTimes)
        {
            var command = processingCommand.Message;

            _ioHelper.TryAsyncActionRecursively <AsyncTaskResult <DomainEventStream> >("FindEventStreamByCommandIdAsync",
                                                                                       () => _eventStore.FindAsync(command.AggregateRootId, command.Id),
                                                                                       currentRetryTimes => HandleExceptionAsync(processingCommand, commandHandler, exception, currentRetryTimes),
                                                                                       result =>
            {
                var existingEventStream = result.Data;
                if (existingEventStream != null)
                {
                    //这里,我们需要再重新做一遍更新内存缓存以及发布事件这两个操作;
                    //之所以要这样做是因为虽然该command产生的事件已经持久化成功,但并不表示已经内存也更新了或者事件已经发布出去了;
                    //因为有可能事件持久化成功了,但那时正好机器断电了,则更新内存和发布事件都没有做;
                    _memoryCache.RefreshAggregateFromEventStore(existingEventStream.AggregateRootTypeName, existingEventStream.AggregateRootId);
                    _eventService.PublishDomainEventAsync(processingCommand, existingEventStream);
                }
                else
                {
                    //到这里,说明当前command执行遇到异常,然后当前command之前也没执行过,是第一次被执行。
                    //那就判断当前异常是否是需要被发布出去的异常,如果是,则发布该异常给所有消费者;否则,就记录错误日志;
                    //然后,认为该command处理失败即可;
                    var publishableException = exception as IPublishableException;
                    if (publishableException != null)
                    {
                        PublishExceptionAsync(processingCommand, publishableException, 0);
                    }
                    else
                    {
                        LogCommandExecuteException(processingCommand, commandHandler, exception);
                        NotifyCommandExecuted(processingCommand, CommandStatus.Failed, exception.GetType().Name, exception.Message);
                    }
                }
            },
                                                                                       () => string.Format("[commandId:{0}]", command.Id),
                                                                                       errorMessage => NotifyCommandExecuted(processingCommand, CommandStatus.Failed, typeof(string).FullName, errorMessage ?? "Get command async failed."),
                                                                                       retryTimes);
        }
        private void TryToRetryCommandForExceptionAsync(ProcessingCommand processingCommand, HandledCommand existingHandledCommand, ICommandHandlerProxy commandHandler, Exception exception, int retryTimes)
        {
            var command = processingCommand.Message;

            //到这里,说明当前command执行遇到异常,然后该command在commandStore中存在,
            //但是在eventStore中不存在,此时可以理解为该command还未被成功执行,此时做如下操作:
            //1.将command从commandStore中移除
            //2.根据eventStore里的事件刷新缓存,目的是为了还原聚合根到最新状态,因为该聚合根的状态有可能已经被污染
            //3.重试该command
            _ioHelper.TryAsyncActionRecursively <AsyncTaskResult>("RemoveCommandAsync",
                                                                  () => _commandStore.RemoveAsync(command.Id),
                                                                  currentRetryTimes => TryToRetryCommandForExceptionAsync(processingCommand, existingHandledCommand, commandHandler, exception, currentRetryTimes),
                                                                  result =>
            {
                _memoryCache.RefreshAggregateFromEventStore(existingHandledCommand.AggregateRootTypeCode, existingHandledCommand.AggregateRootId);
                RetryCommand(processingCommand);
            },
                                                                  () => string.Format("[commandId:{0}]", command.Id),
                                                                  () => NotifyCommandExecuted(processingCommand, CommandStatus.Failed, null, "Remove command async failed."),
                                                                  retryTimes);
        }
        private void HandleExistingHandledCommandForExceptionAsync(ProcessingCommand processingCommand, HandledCommand existingHandledCommand, ICommandHandlerProxy commandHandler, Exception exception, int retryTimes)
        {
            var command         = processingCommand.Message;
            var aggregateRootId = existingHandledCommand.AggregateRootId;

            _ioHelper.TryAsyncActionRecursively <AsyncTaskResult <DomainEventStream> >("FindEventStreamByCommandIdAsync",
                                                                                       () => _eventStore.FindAsync(aggregateRootId, command.Id),
                                                                                       currentRetryTimes => HandleExistingHandledCommandForExceptionAsync(processingCommand, existingHandledCommand, commandHandler, exception, currentRetryTimes),
                                                                                       result =>
            {
                var existingEventStream = result.Data;
                if (existingEventStream != null)
                {
                    _eventService.PublishDomainEventAsync(processingCommand, existingEventStream);
                }
                else
                {
                    LogCommandExecuteException(processingCommand, commandHandler, exception);
                    TryToRetryCommandForExceptionAsync(processingCommand, existingHandledCommand, commandHandler, exception, 0);
                }
            },
                                                                                       () => string.Format("[aggregateRootId:{0}, commandId:{1}]", aggregateRootId, command.Id),
                                                                                       () => NotifyCommandExecuted(processingCommand, CommandStatus.Failed, null, "Find event stream by command id async failed."),
                                                                                       retryTimes);
        }