masstransit

Why does adding UseEntityFrameworkOutbox break UseScheduledRedelivery?


MassTransit UseScheduledRedelivery stops working when UseEntityFrameworkOutbox is added

Problem

I have a MassTransit consumer that handles PostgreSQL concurrent update exceptions using UseScheduledRedelivery. When I use only UseScheduledRedelivery, the redelivery mechanism works correctly for transient failures. However, when I add UseEntityFrameworkOutbox, the redelivery stops working and exceptions go directly to the Fault queue.

Error Details

The PostgreSQL concurrent update error I'm handling:

System.InvalidOperationException: An exception has been raised that is likely due to a transient failure.
 ---> Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
 ---> Npgsql.PostgresException (0x80004005): 40001: could not serialize access due to concurrent update

Current Consumer Configuration

This works (redelivery works correctly):

public class BatchHoldConsumerDefinition : ConsumerDefinition<BatchHoldConsumer>
{
    protected override void ConfigureConsumer
    (
        IReceiveEndpointConfigurator endpointConfigurator,
        IConsumerConfigurator<BatchHoldConsumer> consumerConfigurator,
        IRegistrationContext context
    )
    {
        consumerConfigurator.UseScheduledRedelivery(r => r.ApplyRedeliveryDefaultStrategy());
        // endpointConfigurator.UseEntityFrameworkOutbox<WalletDbContext>(context); // Commented out
    }
}

This doesn't work (messages go to Fault queue):

public class BatchHoldConsumerDefinition : ConsumerDefinition<BatchHoldConsumer>
{
    protected override void ConfigureConsumer
    (
        IReceiveEndpointConfigurator endpointConfigurator,
        IConsumerConfigurator<BatchHoldConsumer> consumerConfigurator,
        IRegistrationContext context
    )
    {
        consumerConfigurator.UseScheduledRedelivery(r => r.ApplyRedeliveryDefaultStrategy());
        endpointConfigurator.UseEntityFrameworkOutbox<WalletDbContext>(context); // Added this line
    }
}

Redelivery Strategy Configuration

public static void ApplyRedeliveryDefaultStrategy(this IRetryConfigurator r, TimeSpan[]? intervals = null)
{
    var redeliveryIntervals = intervals ?? [TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)];
    r.Ignore<MyException>();
    r.Handle<InvalidOperationException>();
    r.Handle<DbUpdateException>();
    r.Intervals(redeliveryIntervals);
}

DbContext Configuration

public sealed class WalletDbContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        modelBuilder.AddInboxStateEntity();
        modelBuilder.AddOutboxMessageEntity();
        modelBuilder.AddOutboxStateEntity();
        
        // ... other entity configurations
    }
}

Questions

  1. Why does adding UseEntityFrameworkOutbox break UseScheduledRedelivery for any exceptions?

  2. Is there a specific order in which these middleware components should be configured?

  3. Is this a known interaction issue between Entity Framework Outbox pattern and MassTransit's scheduled redelivery mechanism?

Environment

Any insights into the proper configuration or the root cause of this behavior would be greatly appreciated!


Solution

  • All of the middleware should be configured on the same place, ie, the endpointConfigurator in the proper order.

    {
        endpointConfigurator.UseScheduledRedelivery(r => r.ApplyRedeliveryDefaultStrategy());
        endpointConfigurator.UseEntityFrameworkOutbox<WalletDbContext>(context); // Added this line
    }