rebus-org / Rebus

:bus: Simple and lean service bus implementation for .NET
https://mookid.dk/category/rebus
Other
2.31k stars 361 forks source link

Polymorphic dispatch not working for 2nd level retries #855

Closed Tsjunne closed 4 years ago

Tsjunne commented 4 years ago

It looks like the polymorphic dispatching only works for the base message types and not the IFailed variant.

I am trying to set up a general configuration for 2nd level retries, but the current design (5.x) doesn't seem to support a centrally configurable strategy.

The best i could do was to implement a base class for message handlers that do the retry routine, like so:

    public abstract class MessageHandler<T> : IHandleMessages<T>, IHandleMessages<IFailed<T>>
    {
        readonly IBus bus;

        public MessageHandler(IBus bus)
        {
            this.bus = bus;
        }

        public abstract Task Handle(T message, CancellationToken cancellation);

        public Task Handle(T message)
        {
            return this.Handle(message, MessageContext.Current.GetCancellationToken());
        }

        public async Task Handle(IFailed<T> message)
        {
            var currentContext = MessageContext.Current;
            int retryCount = 0;

            if (currentContext.Headers.TryGetValue(CustomHeaders.Retries, out var retries))
            {
                retryCount = int.Parse(retries);

                if (retryCount >= 2)
                {
                    await this.bus.Advanced.TransportMessage.Forward("error");
                    return;
                }
            }

            retryCount++;

            await this.bus.Advanced.TransportMessage.Defer(
                TimeSpan.FromSeconds(30 * retryCount),
                new Dictionary<string, string> { { CustomHeaders.Retries, retryCount.ToString() } });
        }
    }

But doing so will throw an exception saying the message cannot be dispatched after the 1st level retries when applying this to a message base class. So i'm left with duplicating my handlers for each concrete type.

Is there any way to configure this behavior globally?

mookid8000 commented 4 years ago

Could you check out this test 👉 https://github.com/rebus-org/Rebus/blob/master/Rebus.Tests/Bugs/PolymorphicDispatchAndSecondLevelRetries.cs and see if it hits the problem you're experiencing?

My suspicion is that your IoC container does not resolve your handler correctly when Rebus tries to look up handlers compatible with FailedMessageWrapper<YourMessage>, because the test passes with Rebus' built-in handler activator.

Tsjunne commented 4 years ago

You are probably right. We are using the 'Rebus.ServiceProvider' extension package. I can reproduce the problem if I modify the example console app from that repo.

I did an attempt to fix the issue and got it to work for the console app, but i have failing tests now and having a hard time figuring out why.

This is the method I changed in https://github.com/rebus-org/Rebus.ServiceProvider/blob/master/Rebus.ServiceProvider/DependencyInjectionHandlerActivator.cs `

    static Type[] FigureOutTypesToResolve(Type messageType)
    {
        IEnumerable<Type> handledMessageTypes;

        if (messageType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFailed<>)))
        {
            var actualMessageType = messageType.GetGenericArguments()[0];
            handledMessageTypes = new[] { messageType }.Concat(actualMessageType.GetBaseTypes().Select(t => typeof(IFailed<>).MakeGenericType(t)));
        }
        else
        {
            handledMessageTypes = new[] { messageType }.Concat(messageType.GetBaseTypes());
        }

        return handledMessageTypes
            .Select(t => typeof(IHandleMessages<>).MakeGenericType(t))
            .ToArray();
    }

` It's possible my attempted solution is rubbish ofcourse, since i don't have a deep inderstanding of the code base yet ...

EDIT: Got it to work, dumb booboo in the code

Tsjunne commented 4 years ago

Looks like a duplicate of https://github.com/rebus-org/Rebus.ServiceProvider/issues/22

mookid8000 commented 4 years ago

Looks like a duplicate of rebus-org/Rebus.ServiceProvider#22

I think you're right, and I believe it was fixed in Rebus.ServiceProvider 5.0.2