예제 #1
0
        // Internal retry for services that subscribe to the multiple events of the same type.
        // It does not interface with routing keys and wildcards (see TryHandleWithRequeuingAsync() below)
        private async Task <Acknowledgement> TryHandleAsync <TMessage>(TMessage message,
                                                                       CorrelationContext correlationContext,
                                                                       Func <Task> handle, Func <TMessage, VirtualMarketException, IRejectedEvent> onError = null)
        {
            var currentRetry = 0;
            var retryPolicy  = Policy
                               .Handle <Exception>()
                               .WaitAndRetryAsync(_retries, i => TimeSpan.FromSeconds(_retryInterval));
            var messageName = message.GetType().Name;

            return(await retryPolicy.ExecuteAsync <Acknowledgement>(async() =>
            {
                var scope = _tracer
                            .BuildSpan("executing-handler")
                            .AsChildOf(_tracer.ActiveSpan)
                            .StartActive(true);
                using (scope)
                {
                    var span = scope.Span;
                    try
                    {
                        var retryMessage = currentRetry == 0
                            ? string.Empty
                            : $"Retry: {currentRetry}'.";
                        var postLogMessage = $"Handling a message : '{messageName}' " +
                                             $"with correlation id: '{correlationContext.Id}' . {retryMessage}";
                        _logger.LogInformation(postLogMessage);
                        span.Log(postLogMessage);
                        return new Ack();
                    }
                    catch (Exception exception)
                    {
                        currentRetry++;
                        _logger.LogError(exception, exception.Message);
                        span.Log(exception.Message);
                        span.SetTag(Tags.Error, true);
                        if (exception is VirtualMarketException virtualException && onError != null)
                        {
                            var rejectedEvent = onError(message, virtualException);
                            await _busClient.PublishAsync(rejectedEvent, ctx => ctx.UseMessageContext(correlationContext));
                            _logger.LogInformation($"Published a rejected event: '{rejectedEvent.GetType().Name}'" +
                                                   $"for the message: '{messageName}' with correlation id: '{correlationContext.Id}'.");

                            span.SetTag("error-type", "domain");
                            return new Ack();
                        }

                        span.SetTag("error-tag", "infrastructure");
                        throw new Exception($"Unable to handle a messge: '{messageName}'" +
                                            $"with correlation id: '{correlationContext.Id}', " +
                                            $" retry {currentRetry - 1}/{_retries} ...");
                    }
                }
            }));
        }
예제 #2
0
        // RabbitMQ retry that will publish a message to the retry queue
        // Kepp in mind that it might get processed by the other services using the same routing key
        // and wildcard
        private async Task <Acknowledgement> TryHandleWithRequeuingAsync <TMessage>(TMessage message,
                                                                                    CorrelationContext correlationContext,
                                                                                    Func <Task> handle, Func <TMessage, VirtualMarketException, IRejectedEvent> onError = null)
        {
            var messageName  = message.GetType().Name;
            var retryMessage = correlationContext.Retries == 0
                ? string.Empty
                : $"Retry: {correlationContext.Retries}'.";

            _logger.LogInformation($"Handling a message: '{messageName}'" +
                                   $"with correlation id: '{correlationContext.Id}' . {retryMessage}");

            try
            {
                await handle();

                _logger.LogInformation($"Handling a message: '{messageName}'" +
                                       $"with correlation id: '{correlationContext.Id}' . {retryMessage}");
                return(new Ack());
            }
            catch (Exception exception)
            {
                _logger.LogError(exception, exception.Message);
                if (exception is VirtualMarketException virtualMarketException && onError != null)
                {
                    var rejectedEvent = onError(message, virtualMarketException);
                    await _busClient.PublishAsync(rejectedEvent, ctx => ctx.UseMessageContext(correlationContext));

                    _logger.LogInformation($"Publish a rejected event: '{rejectedEvent.GetType().Name}'" +
                                           $"for the message: '{messageName}' with correlation id: '{correlationContext.Id}'.");

                    return(new Ack());
                }

                if (correlationContext.Retries >= _retries)
                {
                    await _busClient.PublishAsync(RejectedEvent.For(messageName),
                                                  ctx => ctx.UseMessageContext(correlationContext));

                    throw new Exception($"Unable to handle a message: '{messageName}'" +
                                        $"with correlation id: '{correlationContext.Id}', " +
                                        $"retry {correlationContext.Retries}/{_retries} ...");
                }

                _logger.LogInformation($"Unable to handle a message: '{messageName}'" +
                                       $"with correlation id: '{correlationContext.Id}', " +
                                       $"retry {correlationContext.Retries}/{_retries} ...");

                return(Retry.In(TimeSpan.FromSeconds(_retryInterval)));
            }
        }