Integrating with Akka.NET Persistence
-
This article provides guidance on integrating RavenDB with Akka.Persistence.
-
In this page:
Overview
What is Akka.Net
Akka.NET is a robust set of open-source libraries for building highly concurrent, distributed, and scalable applications on the .NET platform.
It employs the message-driven actor model to simplify concurrency management, resilience and fault isolation, making it easier to develop reliable systems.
What is Akka.Persistence
Akka.Persistence is a library that extends the core functionality of Akka.NET by enabling durable state management for actors.
It allows the creation of actors whose internal state can be persisted and restored after an actor has restarted.
This durability is achieved through event sourcing, where state changes are stored as a sequence of events.
Additionally, optional snapshots can capture the state at specific points in time for quicker recovery.
Upon actor restart, the stored events and snapshots are replayed to restore the actor's internal state.
However, simply including Akka.Persistence only allows for persisting and recovering an actor's state from Akka's default in-memory store. This approach is insufficient if the entire application crashes or restarts, as the in-memory store would be lost.
Using a persistence database plugin
To ensure durability across application restarts, use a dedicated plugin that allows the state to be persisted and replayed from an external database.
Akka.NET supports various persistence stores through a plugin model, which specifies how an actor's state is persisted and recovered.
Ensuring that your actor’s data and any critical messages are persisted and recovered is paramount to building a reliable system. Persistence database plugins play a crucial role by providing the necessary mechanisms to achieve this reliability.
Akka.Persistence.RavenDB
Akka.Persistence.RavenDB is a persistence plugin for Akka.NET that integrates RavenDB as the durable storage backend.
RavenDB is a NoSQL database designed for high performance, scalability, and ease of use.
Among the available plugin options, RavenDB stands out as a highly efficient and flexible choice.
By integrating the RavenDB plugin with Akka.Persistence, you can leverage RavenDB's powerful features
to ensure that your actor state and critical messages are securely persisted and quickly recovered.
With the RavenDB plugin your application can:
- Persist and recover Events to/from a Journal store. Learn more in Events.
- Persist and recover Snapshots to/from a Snapshot store. Learn more in Snapshots.
- Query the stored events. Learn more in Queries.
Installing the RavenDB persistence plugin
Integrate RavenDB with Akka.Persistence using one of the two available NuGet packages:
-
Akka.Persistence.RavenDB
This package allows you to configure the plugin solely through HOCON (Human-Optimized Config Object Notation), which is typically embedded within your app.config or web.config file, or a dedicated HOCON file.# Installing via .NET CLI: dotnet add package Akka.Persistence.RavenDB
-
Akka.Persistence.RavenDB.Hosting
This package includes the base Akka.Persistence.RavenDB, offering greater flexibility by allowing you to configure the plugin through Hosting or via a HOCON configuration file. Using Hosting provides a fast and easy way to set up your app and its persistence without the need to configure HOCON.# Installing via .NET CLI: dotnet add package Akka.Persistence.RavenDB.Hosting
Installing either package will also install the Akka.Persistence package.
When configuring the plugin using both Hosting and HOCON, if the same parameters are specified in both,
the configuration provided via Hosting takes precedence and will override the corresponding HOCON settings.
Configuring the RavenDB persistence plugin with HOCON
-
While both the journal and the snapshot-store share the same configuration keys, they reside in separate scopes.
So when configuring using HOCON, the settings for the journal and snapshot-store must be defined separately,
as shown in the example below. -
For example, properties
urls
andname
can have the same values for both stores,
but they must still be defined distinctly within their respective sections.
Provide different values for each store as needed. -
The following is a sample HOCON configuration under the
<akka>
section.
See the full description of each configuration key below.
<akka>
<hocon>
<![CDATA[
akka.persistence {
# Setup the RavenDB journal store:
journal {
plugin = "akka.persistence.journal.ravendb"
ravendb {
class = "Akka.Persistence.RavenDb.Journal.RavenDbJournal, Akka.Persistence.RavenDb"
plugin-dispatcher = "akka.actor.default-dispatcher"
urls = ["http://localhost:8080"]
name = "MyAkkaStorageDB"
auto-initialize = false
#certificate-path = "\\path\\to\\cert.pfx"
#save-changes-timeout = 30s
#http-version = "2.0"
#disable-tcp-compression = false
}
}
# Setup the RavenDB snapshot store:
snapshot-store {
plugin = "akka.persistence.snapshot-store.ravendb"
ravendb {
class = "Akka.Persistence.RavenDb.Snapshot.RavenDbSnapshotStore, Akka.Persistence.RavenDb"
plugin-dispatcher = "akka.actor.default-dispatcher"
urls = ["http://localhost:8080"]
name = "MyAkkaStorageDB"
auto-initialize = false
#certificate-path = "\\path\\to\\cert.pfx"
#save-changes-timeout = 30s
#http-version = "2.0"
#disable-tcp-compression = false
}
}
query {
# Configure RavenDB as the underlying storage engine for querying:
ravendb {
class = "Akka.Persistence.RavenDb.Query.RavenDbReadJournalProvider, Akka.Persistence.RavenDb"
#refresh-interval = 3s
#max-buffer-size = 65536
}
}
}
]]>
</hocon>
</akka>
Configuration keys
Journal and snapshot config keys
Predefined plugins and class names to use:
- journal.plugin
The fully qualified name of the RavenDB plugin to be used for the journal store.
Value to set:"akka.persistence.journal.ravendb"
- journal.ravendb.class
The fully qualified class name for the RavenDB persistence journal actor.
Value to set:"Akka.Persistence.RavenDb.Journal.RavenDbJournal, Akka.Persistence.RavenDb"
- snapshot-store.plugin
The fully qualified name of the RavenDB plugin to be used for the snapshot store.
Value to set:"akka.persistence.snapshot-store.ravendb"
- snapshot-store.ravendb.class
The fully qualified class name for the RavenDB persistence snapshot actor.
Value to set:"Akka.Persistence.RavenDb.Snapshot.RavenDbSnapshotStore, Akka.Persistence.RavenDb"
Common config keys for journal and snapshot-store:
- plugin-dispatcher
The dispatcher responsible for managing the thread pool and scheduling tasks for the actor.
Default:"akka.actor.default-dispatcher"
- urls
An array of server URLs where the RavenDb database is stored.
Default: No default, param must be provided.
e.g.:["http://localhost:8080"]
- name
The name of the database where the persistence data should be stored.
It is recommended to create a separate database for Akka storage, distinct from your other work databases.
Default: No default, param must be provided.
e.g.:"MyAkkaStorageDB"
- auto-initialize
Create the database if it doesn't exist.
No exception is thrown if the database already exists.
Default:false
- certificate-path
Location of a client certificate to access a secure RavenDB database.
If a password is required, it should be stored in theRAVEN_CERTIFICATE_PASSWORD
env variable.
Default:null
e.g.:"\\path\\to\\cert.pfx"
- save-changes-timeout
Timeout for 'save' requests sent to RavenDB, such as writing or deleting
as opposed to stream operations which may take longer and have a different timeout (12h).
Client will fail requests that take longer than this.
Default:30s
- http-version
Http version for the RavenDB client to use in communication with the server.
Default:"2.0"
- disable-tcp-compression
Determines whether to compress the data sent in the client-server TCP communication.
Default:false
Query config keys
- query.ravendb.class
The fully qualified class name for the RavenDB journal provider.
Value to set:"Akka.Persistence.RavenDb.Query.RavenDbReadJournalProvider, Akka.Persistence.RavenDb"
- refresh-interval
The interval at which to check for new ids/events.
Default:3s
- max-buffer-size
The number of events to keep buffered while querying until they are delivered downstream.
Default:65536
Configuring the RavenDB persistence plugin with Hosting
-
Using Hosting, you can easily set up the RavenDB plugin during your application's startup.
-
Use method
WithRavenDbPersistence
to configure all relevant parameters.
See the available parameters and method overloads in the syntax section below. -
The following example shows a basic configuration using Hosting:
// Add the following using statements:
using Microsoft.Extensions.Hosting;
using Akka.Hosting;
using Akka.Persistence.Hosting;
using Akka.Persistence.RavenDb.Hosting;
namespace Raven.Documentation.Samples.Integrations.AkkaPersistence
{
class Program
{
static void Main(string[] args)
{
var host = new HostBuilder().ConfigureServices((context, services) =>
{
services.AddAkka("my-actor-system-name", (builder, provider) =>
{
// Call 'WithRavenDbPersistence' to configure RavenDB as the persistence storage
builder.WithRavenDbPersistence(
// URL of the RavenDB server
urls: new[] { "http://localhost:8080" },
// The database where the journal events and the snapshots will be persisted
databaseName: "MyAkkaStorageDB",
// Configuration will apply to both the journal and the snapshot stores
mode: PersistenceMode.Both);
});
});
var app = host.Build();
app.Run();
}
}
}
Syntax
// A simple overload providing basic configuration
// ===============================================
public static AkkaConfigurationBuilder WithRavenDbPersistence(
this AkkaConfigurationBuilder builder,
// An array of server URLs where the RavenDB database is stored.
string[] urls,
// The name of the database where the persistence data should be stored.
// It is recommended to create a separate database for Akka storage,
// distinct from your other work databases.
string databaseName,
// Location of a client certificate to access a secure RavenDB database.
// If a password is required, it should be stored in the RAVEN_CERTIFICATE_PASSWORD env var.
string? certificatePath = null,
// Create the database if it doesn't exist.
bool autoInitialize = true,
// Determines whether this configuration will be applied to the Journal store,
// the Snapshot store, or both stores.
PersistenceMode mode = PersistenceMode.Both,
string pluginIdentifier = "ravendb",
bool isDefaultPlugin = true,
Action<AkkaPersistenceJournalBuilder>? journalBuilder = null)
public enum PersistenceMode
{
// Sets both the akka.persistence.journal and the akka.persistence.snapshot-store to use this plugin.
Both,
// Sets ONLY the akka.persistence.journal to use this plugin.
Journal,
// Sets ONLY the akka.persistence.snapshot-store to use this plugin.
SnapshotStore,
}
// These overloads allow for applying separate configurations to the Journal and Snapshot stores
// =============================================================================================
public static AkkaConfigurationBuilder WithRavenDbPersistence(
this AkkaConfigurationBuilder builder,
Action<RavenDbJournalOptions>? journalOptionConfigurator = null,
Action<RavenDbSnapshotOptions>? snapshotOptionConfigurator = null,
bool isDefaultPlugin = true)
public static AkkaConfigurationBuilder WithRavenDbPersistence(
this AkkaConfigurationBuilder builder,
RavenDbJournalOptions? journalOptions = null,
RavenDbSnapshotOptions? snapshotOptions = null)
// Use this class to define the Journal store configuration
public class RavenDbJournalOptions
{
public string? Name { get; set; }
public string[] Urls { get; set; }
public string? CertificatePath { get; set; }
// Http version for the RavenDB client to use in communication with the server
public Version? HttpVersion { get; set; }
// Determines whether to compress the data sent in the client-server TCP communication
public bool? DisableTcpCompression { get; set; }
// Timeout for 'save' requests sent to RavenDB, such as writing or deleting
// as opposed to stream operations which may take longer and have a different timeout (12h).
// Client will fail requests that take longer than this.
public TimeSpan? SaveChangesTimeout { get; set; }
}
// Use this class to define the Snapshot store configuration
public class RavenDbSnapshotOptions
{
public string? Name { get; set; }
public string[] Urls { get; set; }
public string? CertificatePath { get; set; }
public Version? HttpVersion { get; set; }
public bool? DisableTcpCompression { get; set; }
public TimeSpan? SaveChangesTimeout { get; set; }
}