Rhino.ServiceBus saga persistence with RavenDB

I want to document the RavenDB saga persister that I added to github about a year ago.  Thanks to Corey Kaylor for his help with writing the initial code.

RavenDB is a natural fit for saga state storage and the client’s support for dynamic objects makes it very easy to do.  Setting up your project to use this persister requires a couple of registrations add to the service bus bootstrapper.  The following shows an example using WIndsor:

The RavenStoreProviderMessageModule lazily creates IDocumentSessions scoped to the processing of a message when the saga persister is invoked.  It does this using the IDocumentStore injected into it, so you can configure which document store you want saga state persisted however you choose.

The RavenSagaPersister handles loading and saving saga state using an IDocumentSession.  It sets the document ID using the convention ‘{saga type}/{saga ID}’.  For example: MySagaType/25C209BD-F655-4526-92E0-6A2CB59AEAE2.

You can view the full RavenSagaPersister on github here.

Please note: The project’s dependencies are currently out of date, so it’ll need to be recompiled with the version of RavenDB and Rhino ServiceBus you’re using or just add an assembly redirect to your project’s config.

Advertisements

Rhino.ServiceBus 3.0 Delivery Options

This post delves into the details of a feature I contributed to Rhino.ServiceBus, available in the recently released version 3.0.  Previously, you were able to add custom headers to outgoing messages by registering an implementation of ICustomizeMessageHeaders with the container.  That interface has been renamed to ICustomizeOutgoingMessages, and now allows you to also set a DeliverBy and/or MaxAttempts.  You can register any number of implementations with the container, and the bus will invoke each one as it builds outgoing messages.

Rhino.Queues has full support for DeliverBy and MaxAttempts.  If either of these two options is set, the message will be moved to the OutgoingHistory and marked as failed transmission if the message couldn’t be delivered in time.

MSMQ supports DeliverBy by setting the TimeToReachQueue property on the MSMQ message.  MaxAttempts is only supported when set to 1, which sets TimeToReachQueue to TimeSpan.Zero, meaning MSMQ only makes one attempt to deliver the message.  If MaxAttempts is set to a value other than 1, an InvalidUsageException is thrown.

Because it’s up to the user to create an implementation of ICustomizeOutgoingMessages, the user can set delivery options in a way that works best for them, whether it’s by setting options for specific message types, specific destinations, or reflecting over messages to look for specific attributes or interfaces.

Examples:

If you want to limit how many times the bus tries delivering messages to endpoints that aren’t always online, you could add a query parameter to the endpoint and the customize using the Destination on the OutgoingMessageInformation.

 

Maybe you have a message that’s only useful for a short period of time.

 

Using the OutgoingMessageInformation instance, you can inspect the Messages being sent, the Destination endpoint, and the Source endpoint to make any customizations you need.  You can modify the Headers collection on that instance, set MaxAttempts, or set DeliverBy.