1

I am using the RedisMessageQueueClient as can be seen here:

    public TResponse SendSync<TRequest, TResponse>(TRequest request, int? timeoutMilliseconds = null)
        where TRequest : CoreRequest 
        where TResponse : CoreRequest
    {
        IMessage responseMessage = null;
        using (var mqClient = MqClientFactory.Instance.CreateMessageQueueClient())
        {
            // mqClient is ServiceStack.Messaging.RedisMessageQueueClient

            var uniqueCallbackQ = $"mq:c1:{request.GetType().Name}:{Guid.NewGuid():N}";
            var clientMsg = new Message<TRequest>(request)
            {
                ReplyTo = uniqueCallbackQ,
                RetryAttempts = 0
            };
    
            mqClient.Publish(clientMsg);
    
            TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds.HasValue ? timeoutMilliseconds.Value : 120000);
    
            //Blocks thread on client until reply message is received
            responseMessage = mqClient.Get<TResponse>(uniqueCallbackQ, timeout);
    
            if(responseMessage?.Body == null)
            {
                throw new TimeoutException($"Request {request.GetType().Name} from {Assembly.GetEntryAssembly().GetName().Name} has timed out!");
            }
    
        }
        return responseMessage?.Body as TResponse;
    }

for some reason in my code, an error is produced (this is what I am actually trying to find), and according to this SO post, that error should be returned to the ReplyTo address:

If you're using an explicit ReplyTo address any Errors will be sent to that ReplyTo address instead of the DLQ.

In this case, I am using the ReplyTo, like this:

ReplyTo is set, as well as the RetryAttempts

However, when I browse Redis, I see that the request has ended in in a DLQ, but not the ReplyTo address. In the image below, we see that:

  1. The ReplyTo address is set and it is the same as in the code above
  2. The RetryAttempts is 0 in code, but 2 in the DQL dump; I also see that the failed request is resent 2 more times. The MqServer is created using _mqServer = new RedisMqServer(_redisClientManager, retryCount: 2), but I expected that I could override this, using the code above? I also changed it to _mqServer = new RedisMqServer(_redisClientManager, retryCount: 0), but it still retried twice.

enter image description here

.NET 5.0, ServiceStack.Redis.Core 5.10.4, Visual Studio 2019

Ted
  • 19,727
  • 35
  • 96
  • 154

1 Answers1

1

This is still the case where failed Error Responses are sent to the ReplyMq:

using (var mqFactory = appHost.TryResolve<IMessageFactory>())
{
    var request = new ThrowGenericError { Id = 1 };

    using (var mqProducer = mqFactory.CreateMessageProducer())
    using (var mqClient = mqFactory.CreateMessageQueueClient())
    {
        var requestMsg = new Message<ThrowGenericError>(request)
        {
            ReplyTo = $"mq:{request.GetType().Name}.replyto"
        };
        mqProducer.Publish(requestMsg);

        var msg = mqClient.Get<ErrorResponse>(requestMsg.ReplyTo, null);
        mqClient.Ack(msg);

        Assert.That(msg.GetBody().ResponseStatus.ErrorCode, Is.EqualTo("ArgumentException"));
    }
}

Failed responses are sent immediately to ReplyTo, i.e. they're not retried so it's not clear where your DLQ messages are coming from.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • Hello! First; any idea regarding the retry issue? Thanks. – Ted Aug 28 '21 at 10:06
  • @Ted What retry issue? ReplyTo messages shouldn't retry. – mythz Aug 28 '21 at 10:48
  • 1
    Very strange, cause it does retry sending that ActorLogin twice, so total 3 times, regardless if the ReplyTo is set... Im gonna do some more investigating, and if the problem persists, I will create a small repro project and update here. – Ted Aug 28 '21 at 10:53
  • This seems to be the issue: i I override `public virtual object OnAfterExecute(IRequest req, object requestDto, object response)` and there is an Exception happening there, then the above problem seems to happen; the error ends up in the DLQ, and is not returned to sender (in current implementation, the `base.OnAfterExecute(...)` was the last line in the method, and was thus not executed due to the exception. Am I correct here? If so, is this intended behaviour? – Ted Aug 28 '21 at 11:09
  • @Ted Potentially `OnAfterExecute()` doesn't fire for Exceptions in Service impls and it shouldn't be throwing Exceptions itself. Try debugging into the fx to confirm, here's the [code path for Exceptions with ReplyTo messages](https://github.com/ServiceStack/ServiceStack/blob/cac8e8f69ed7983ade106fcfa4579508f93bd198/src/ServiceStack/Messaging/MessageHandler.cs#L146), if it ends up in the DLQ it's not taking that expected code path. – mythz Aug 28 '21 at 11:32
  • If the Service itselfs throws an exception, I dont see the problem. But if there is an uncaught Exception inside the override OnAfterExecute, then it looks to me like the message is never returned to sender, but ends up in the DLQ, as shown above. I have confirmed this by throwing exceptions in OnAfterExecute, and catching those exceptions and thus not throwing anything. If Exception is thrown inside OnAfterExecute, it seem that retries are made, regardless of what the config says. – Ted Aug 28 '21 at 11:55