Scheduled Message Delivery with Wolverine

Wolverine has the ability to schedule the delivery of messages for a later time. While Wolverine certainly isn’t trying to be Hangfire or Quartz.Net, the message scheduling in Wolverine today is valuable for “timeout” messages in sagas, or “retry this evening” type scenarios, or reminders of all sorts.

If using the Azure Service Bus transport, scheduled messages sent to Azure Service Bus queues or topics will use native Azure Service Bus scheduled delivery. For everything else today, Wolverine is doing the scheduled delivery for you. To make those scheduled messages be durable (i.e. not completely lost when the application is shut down), you’re going to want to add message persistence to your Wolverine application as shown in the sample below using SQL Server:

// This is good enough for what we're trying to do
// at the moment
builder.Host.UseWolverine(opts =>
{
    // Just normal .NET stuff to get the connection string to our Sql Server database
    // for this service
    var connectionString = builder.Configuration.GetConnectionString("SqlServer");
    
    // Telling Wolverine to build out message storage with Sql Server at 
    // this database and using the "wolverine" schema to somewhat segregate the 
    // wolverine tables away from the rest of the real application
    opts.PersistMessagesWithSqlServer(connectionString, "wolverine");
    
    // In one fell swoop, let's tell Wolverine to make *all* local
    // queues be durable and backed up by Sql Server 
    opts.Policies.UseDurableLocalQueues();
});

Finally, with all that said, here’s one of the ways to schedule message deliveries:

    public static async Task use_message_bus(IMessageBus bus)
    {
        // Send a message to be sent or executed at a specific time
        await bus.ScheduleAsync(new DebitAccount(1111, 100), DateTimeOffset.UtcNow.AddDays(1));

        // Or do the same, but this time express the time as a delay
        await bus.ScheduleAsync(new DebitAccount(1111, 225), 1.Days());
        
        // ScheduleAsync is really just syntactic sugar for this:
        await bus.PublishAsync(new DebitAccount(1111, 225), new DeliveryOptions { ScheduleDelay = 1.Days() });
    }

Or, if you want to utilize Wolverine’s cascading message functionality to keep most if not all of your handler method signatures “pure”, you can use this syntax within message handlers or HTTP endpoints:

    public static IEnumerable<object> Consume(Incoming incoming)
    {
        // Delay the message delivery by 10 minutes
        yield return new Message1().DelayedFor(10.Minutes());

        // Schedule the message delivery for a certain time
        yield return new Message2().ScheduledAt(new DateTimeOffset(DateTime.Today.AddDays(2)));
    }

Finally, one last alternative that was primarily meant for saga usage, subclassing TimeoutMessage like so:

public record EnforceAccountOverdrawnDeadline(Guid AccountId) : TimeoutMessage(10.Days()), IAccountCommand;

By subclassing TimeoutMessage, the message type above is “scheduled” for a later time when it’s returned as a cascading message.

Leave a comment