Search code examples
.netnservicebusnservicebus6

Timeout manager retuning timout immediately


I have a very simple scheduler saga, that is supposed to send a specific message every day. It is implemented as a saga that requests a timeout. when the timeout is handled an action is executed (a message is sent) and a new timeout is requested the next day.

I have done the exact same thing before with success, but now the timeout seems to trigger immediately so matter what DateTime is requested.

The endpoint is self-hosting, and configured to use InMemoryPersistence. NServiceBus version is 6.4.3.

The saga is implemented as follows. I have removed all the logic, but still timeout messages are received immediately and infinitely.

public class SchedulerSaga: Saga<SchedulerState>,
    IAmStartedByMessages<StartSchedulerSagaCommand>,
    IHandleTimeouts<SchedulerTimeout>
{
    private readonly IConfigurationProvider _config;

    public SchedulerSaga(IConfigurationProvider config)
    {
        _config = config;
    }

    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SchedulerState> mapper)
    {
        mapper.ConfigureMapping<StartSchedulerSagaCommand>(_ => _.SchedulerName).ToSaga(_ => _.SchedulerName);
    }

    public async Task Handle(StartSchedulerSagaCommand message, IMessageHandlerContext context)
    {
        Data.SchedulerName = message.SchedulerName;
        await StartProcessAndScheduleNewTimeout(context);
    }

    public async Task Timeout(SchedulerTimeout state, IMessageHandlerContext context)
    {
        Data.Counter++;
        await StartProcessAndScheduleNewTimeout(context);
    }

    private async Task StartProcessAndScheduleNewTimeout(IMessageHandlerContext context)
    {
        await RequestTimeout(context, new DateTime(2018, 9, 16, 0, 0, 0, DateTimeKind.Utc), new SchedulerTimeout { Counter = Data.Counter });
    }
}

The endpoint is configured as follows:

    public static EndpointConfiguration ConfigureMsmqEndpoint(IWindsorContainer container)
    {
        var endpointConfiguration = new EndpointConfiguration(MsmqEndpointName);

        ConfigureRouting(endpointConfiguration);

        endpointConfiguration.UsePersistence<InMemoryPersistence>();

        endpointConfiguration.SendFailedMessagesTo($"{MsmqEndpointName}.error");
        endpointConfiguration.AssemblyScanner().ExcludeAssemblies("tools");
        endpointConfiguration.EnableInstallers();

        ConfigureUnobtrusiveMessageConvention(endpointConfiguration);

        endpointConfiguration.Recoverability().Delayed(DelayedSettings);

        endpointConfiguration.UseContainer<WindsorBuilder>(c => c.ExistingContainer(container));

        return endpointConfiguration;
    }

I also tried to use the built in Scheduling mechanism and the same thing happens, hundereds of timeouts are triggered every second.

await endpointInstance.ScheduleEvery(
        timeSpan: TimeSpan.FromMinutes(5),
        task: context=> context.SendLocal(new SomeMessage())
    )
.ConfigureAwait(false);

Update: Add repo with code reproducing the problem.

https://github.com/spinakr/nsb-scheduling-msmq

The problem only occur when the package "NServiceBus.Azure.Transports.WindowsAzureStorageQueues" is references in the project, even if it is not used!

The app in review has two endpoint hosted in the same process. Consuming messages from MSMQ and Azure storage queues. In the repo, it is only when adding the azure storage queues transport package that the issue start occurring.


Solution

  • I assume that the endpoint you are describing are using MSMQ as transport (based on names of methods and fields) and that the saga is running on that endpoint.

    MSMQ relies on the Timeout Manager to support delayed delivery. The Azure Storage Queues transport, on the other hand, seems to solve delayed delivery in a different way. It actually has a feature, enabled by default, that prevents deferred messages from being routed to the Timeout Manager. If your MSMQ endpoint were to scan the NServiceBus.Azure.Transports.WindowsAzureStorageQueues assembly, your deferred messages would not reach the Timeout Manager.

    One solution to this issue would be to configure the assembly scanner in your MSMQ endpoint to exclude that assembly:

    endpointConfiguration.AssemblyScanner().ExcludeAssemblies(
        "NServiceBus.Azure.Transports.WindowsAzureStorageQueues.dll");