Search code examples
nservicebus

Nsb for certain type of exceptions: skip SLR + ability to ignore error queue


I want to have a central place to implement exceptions handling logic for specific type of exception.

If specific exception type occurs, I'd like to be able to run one of the following depending on internal configuration:

  • send message to error queue immediately without further second level retries;
  • hide the message, not sending it to both processing queue or error queue;

I've found this topic which covers first case but not second one, as a message would be put into error queue if we return TimeSpan.MinValue: NServiceBus error handling

So how could I implement 2nd case? better both to be be implemented in one place, one class


Solution

  • Prior to version 6 of NServiceBus, you could use IManageMessageFailures to manage message failures. You can handle the case of a serialization exception or - more relevant to your problem at hand - when a message can not be handled gracefully after first-level retries are attempted.

    Here's how to implement a custom FaultManager that'd ignore exceptions of certain type or send failed messages with other errors back to the error queue. Note that the First-Level retires still happens and this kicks-in instead of Second-Level retry.

    public class IssueOrder : ICommand
    {
        public bool NotFound { get; set; }
        public bool HasFaulted { get; set; }
    }
    
    public class OrderHandler : IHandleMessages<IssueOrder>
    {
        public void Handle(IssueOrder message)
        {
            if(message.NotFound)
                throw new OrderNotFoundException();
    
            if(message.HasFaulted)
                throw new ApplicationException();
        }
    }
    
    public class OrderNotFoundException : Exception
    {
    }
    
    public class CustomFaultManager : IManageMessageFailures
    {
        private ISendMessages sender;
        private MessageForwardingInCaseOfFaultConfig config;
        private BusNotifications notifications;
        private static ILog Logger = LogManager.GetLogger<CustomFaultManager>();
    
        public CustomFaultManager(ISendMessages sender, IProvideConfiguration<MessageForwardingInCaseOfFaultConfig> config)
        {
            this.sender = sender;
            this.config = config.GetConfiguration();
        }
    
        public void SerializationFailedForMessage(TransportMessage message, Exception e)
        {
        }
    
        public void ProcessingAlwaysFailsForMessage(TransportMessage message, Exception e)
        {
            if (e is OrderNotFoundException)
            {
                //Ignore the exception;
                Logger.WarnFormat("OrderNotFoundException was thrown. Ignoring the message Id {0}.", message.Id);
            }
            else
            {
                //Check if you have performed enough retries, ultimately send to error queue
                SendToErrorQueue(message, e);
            }
        }
    
        private void SendToErrorQueue(TransportMessage message, Exception ex)
        {
            message.TimeToBeReceived = TimeSpan.MaxValue;
            sender.Send(message, new SendOptions(config.ErrorQueue));
            Logger.WarnFormat("Message {0} will was moved to the error queue.", message.Id);
        }
    
        public void Init(Address address)
        {
        }
    }
    

    And to register the custom FaultManager:

    var config = new BusConfiguration();
    //Other configuration code
    config.RegisterComponents(c =>
            {
                c.ConfigureComponent<CustomFaultManager>(DependencyLifecycle.InstancePerCall);
            });
    

    In Version 6 of NServiceBus however, the IManageMessageFailures interface is deprecated. The new Recoverability api in version 6 allows for better customization, althrough there's no direct way of ignoring/muting an exception. For that purpose you need a custom behavior in the NServiceBUs pipeline and run it in a step between one of the known steps (e.g. before a message is moved to the error queue).