rebus-org / Rebus.SqlServer

:bus: Microsoft SQL Server transport and persistence for Rebus
https://mookid.dk/category/rebus
Other
43 stars 42 forks source link

Question: Message could not be dispatched to any handlers (and will not be retried under the default fail-fast settings) #92

Closed mohdali closed 2 years ago

mohdali commented 2 years ago

Hi,

I'm new to Rebus and want to use SQL as transport since the throughput requirement is not high and I don't want to introduce MSMQ or RabbitMQ at this stage.

I have configured on both sender and receiver as below (two different applications running in IIS):

var handleActivator = new DependencyInjectionHandlerActivator(serviceProvider);

            var configurer = Configure.With(handleActivator);

            configurer.Routing(r => r.TypeBased().Map<ReportEvent>(queueName))
                    .Logging(l => l.MicrosoftExtensionsLogging(
                                serviceProvider.GetRequiredService<ILoggerFactory>()))
                    .Subscriptions(s=> s.StoreInSqlServer(connectionString, subscriptionName, isCentralized: true))
                    .Transport(t =>
                    {
                        var options = new SqlServerTransportOptions(connectionString);
                        options.SetEnsureTablesAreCreated(true);

                        t.UseSqlServer(options, queueName);                        
                    })
                    .Options(o =>

                    {
                        o.SimpleRetryStrategy(errorQueueAddress: errorQueueName);
                    });

            this.ServiceBus = configurer.Start();

Then, on receiver end I added the handler as below:

services.AutoRegisterHandlersFromAssemblyOf<ReportMessageHandler>();

services.AddTransient<IHandleMessages<ReportEvent>, ReportMessageHandler>();

The setup works, but not consistently.

I frequently get the error message mentioned in the subject

System.AggregateException: 1 unhandled exceptions (Message with ID 7c1cd72f-2cc7-4d69-b904-1af7e42f200f and type ReportEvent, Reports.Core could not be dispatched to any handlers (and will not be retried under the default fail-fast settings))
 ---> Rebus.Exceptions.MessageCouldNotBeDispatchedToAnyHandlersException: Message with ID 7c1cd72f-2cc7-4d69-b904-1af7e42f200f and type ReportEvent, Reports.Core could not be dispatched to any handlers (and will not be retried under the default fail-fast settings)
   at Rebus.Pipeline.Receive.DispatchIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.Sagas.LoadSagaDataStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.Pipeline.Receive.ActivateHandlersStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.Pipeline.Receive.HandleRoutingSlipsStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.Pipeline.Receive.DeserializeIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.DataBus.ClaimCheck.HydrateIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.Pipeline.Receive.HandleDeferredMessagesStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.Retry.FailFast.FailFastStep.Process(IncomingStepContext context, Func`1 next)
   at Rebus.Retry.Simple.SimpleRetryStrategyStep.DispatchWithTrackerIdentifier(Func`1 next, String identifierToTrackMessageBy, ITransactionContext transactionContext, String messageId, String secondLevelMessageId)
   --- End of inner exception stack trace ---

I tried sending messages with both Send() and Publish() methods but I get the same outcome. So I'm not sure what am I missing and how to avoid this issue?

I'm using Rebus 6.6.1 and Rebus.SqlServer 7.2.0

mookid8000 commented 2 years ago

First off, I just want to direct your attention to the AddRebus extension on IServiceCollection – it makes the configuration a little but smoother:

services.AddRebus((configure, provider) => 
    configure
        .Transport(t => t.Use(...))
        .Routing(r => (...)
);

and then

app.ServiceProvider.UseRebus();

later on to start the bus. Moreover, there's

services.AddRebusHandler<ReportMessageHandler>();

to register handlers in just the right way. 🙂

Regarding the error, my initial guess is that you've configured your two instances to use the same queue? If this is the case, they will compete for the same messages, and thus sometimes end up snatshing a messages that cannot be handled.

mohdali commented 2 years ago

Hi,

Thank for your assessment and reply.

The reason I'm not using services.AddRebus() and app.ApplicationService.UseRebus() is that there are already other buses configured using in memory transport which I don't want to interfere with.

Instead, I want only this bus to use SqlServer as a transport since it is running across two applications running in IIS and I'm not in a Position to introduce MSMQ or RabbitMQ at this stage.

It seems to me that the producer is failing immediately because it cannot find any handlers. I have configured the handler only in the consumer side. But I'm not sure how it works. Basically I'm assuming the producer will push the message to the queue table and the consumer will eventually handle it. Instead, seems to me the producer is trying to find the handler, since it cannot find it immediately puts in error queue.

Can you please advise how to configure this scenario?

mookid8000 commented 2 years ago

It seems to me that the producer is failing immediately because it cannot find any handlers. I have configured the handler only in the consumer side. But I'm not sure how it works. Basically I'm assuming the producer will push the message to the queue table and the consumer will eventually handle it. Instead, seems to me the producer is trying to find the handler, since it cannot find it immediately puts in error queue.

Can you please advise how to configure this scenario?

Yes, be sure to configure the producer and consumer each to have its own input queue.

E.g. called "producer" for the producer.

Alternatively, don't give the producer an input queue - since it's not going to receive any messages, you can configure it as a "one-way client":

    .Transport(t => t.UseSqlServerAsOneWayClient(...))
    .Subscriptions(s=> s.StoreInSqlServer(connectionString, subscriptionName, isCentralized: true))

You then configure the consumer with its own input queue, e.g. called "consumer"

    .Transport(t => t.UseSqlServer(..., "consumer"))
    .Subscriptions(s=> s.StoreInSqlServer(connectionString, subscriptionName, isCentralized: true))

and then in the consumer, you can

await bus.Subscribe<ReportMessage>();

and in the producer you can

await bus.Publish(new ReportMessage(...));
mohdali commented 2 years ago

Thank you very much!

This worked perfectly.

Appreciate your timely support and clarification.