A distributed saga implementation for Orleans
Orleans.Sagas sits on top of the Orleans framework, enabling sagas to scale up across a cluster of machines. However the Orleans.Sagas library does not require any detailed knowledge of Orleans. It is primarily designed to make it effortless to write robust and horizontally scalable sagas (compensatable workflows) for .NET with very minimal knowledge of distributed systems.
If you are familiar with Orleans you can use Orleans.Sagas to orchestrate long running workflows which involve Orleans grains, or a combination of grains and external services.
The library is currently in user acceptance testing with its first major consumer and is therefore not recommended for production use yet and will likely require subsequent API changes prior to a supported release.
The NuGet package is available as a preview, and in the absence of detailed documentation, the gitter chat is a good place to start.
- Garcia-Molina, H. - Sagas (1987)
- Eldeeb, T. and Bernstein, P. - Transactions for Distributed Actors in the Cloud (2016)
- McCaffrey, C. - Applying the Saga Pattern (2015)
Install the NuGet package with a package manager.
- Configure a default storage provider
- Configure a reminder storage provider
- Add the following statements to your Orleans silo builder:
.UseOrleans(siloBuilder =>
{
siloBuilder
.UseSagas()
.ConfigureApplicationParts(parts =>
{
parts.AddSagaParts();
})
...
}
public class BookHireCarActivity : IActivity
{
public async Task Execute(IActivityContext context)
{
// idempotently request a hire car from a hire car service.
await HireCarService.Book(context.SagaId, context.SagaProperties.Get<int>("HireCarModel"));
}
public async Task Compensate(IActivityContext context)
{
// idempotently cancel a hire car request from a hire car service.
await HireCarService.Cancel(context.SagaId);
}
}
public class BookHireCarActivity : IActivity
{
public BookHireCarActivity(IWasAddedAsAServiceOnStartUp myDependency)
{
}
}
// create a saga builder.
var sagaBuilder = GrainFactory.CreateSaga();
// add activities to your saga, and optional associated configuration.
sagaBuilder
.AddActivity<BookHireCarActivity>()
.AddActivity<BookHotelActivity>()
.AddActivity<BookPlaneActivity>()
// execute the saga (idempotent).
var saga = await sagaBuilder.ExecuteSagaAsync(x => x.Add("HireCarModel", 1));
// abort the saga (idempotent).
await saga.RequestAbort();