Orleans.EventSourcing.Kurrent 10.1.1-preview4

This is a prerelease version of Orleans.EventSourcing.Kurrent.
dotnet add package Orleans.EventSourcing.Kurrent --version 10.1.1-preview4
                    
NuGet\Install-Package Orleans.EventSourcing.Kurrent -Version 10.1.1-preview4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Orleans.EventSourcing.Kurrent" Version="10.1.1-preview4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Orleans.EventSourcing.Kurrent" Version="10.1.1-preview4" />
                    
Directory.Packages.props
<PackageReference Include="Orleans.EventSourcing.Kurrent" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Orleans.EventSourcing.Kurrent --version 10.1.1-preview4
                    
#r "nuget: Orleans.EventSourcing.Kurrent, 10.1.1-preview4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Orleans.EventSourcing.Kurrent@10.1.1-preview4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Orleans.EventSourcing.Kurrent&version=10.1.1-preview4&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Orleans.EventSourcing.Kurrent&version=10.1.1-preview4&prerelease
                    
Install as a Cake Tool

Orleans.EventSourcing.Kurrent

NuGet

Microsoft Orleans providers backed by KurrentDB (formerly EventStoreDB)

This project adds KurrentDB support to Microsoft Orleans.

Provider Orleans interface What it persists When to use
Cluster membership provider (UseKurrentClustering) IMembershipTable IGatewayListProvider Cluster membership information Use this to maintain cluster membership in KurrentDB.
Log-consistency provider (AddKurrentBasedLogConsistencyProvider*) ILogViewAdaptorFactory for JournaledGrain<TView, TEntry> The full event log for the grain — every RaiseEvent is appended to the grain's Kurrent stream. The view is rebuilt by replaying events. Event-sourced grains (JournaledGrain). Gives you full history, projections via IGrainEventProvider, and replay.
Grain state storage provider (AddKurrentBasedGrainStorageProvider*) IGrainStorage for Grain<TState> / [PersistentState] The latest snapshot only — each WriteStateAsync appends one event and Kurrent's MaxCount = 1 stream metadata trims older revisions. Regular Orleans state persistence when you want it backed by Kurrent (e.g. to keep all storage in one system)
Reminders provider (AddKurrentReminderService) ⚠ experimental IReminderTable Grain Reminders Use this provider to persist and manage Orleans reminders in KurrentDB. Installed from a separate package Orleans.EventSourcing.Kurrent.Reminders.
Grain event subscription (IGrainEventProvider) ⚠ experimental n/a Read-side subscription to the Kurrent $all stream, filtered to the streams produced by the log-consistency provider. Projections / read models built from grain events emitted via the log-consistency provider.

Install

dotnet add package Orleans.EventSourcing.Kurrent

The reminders provider is shipped as a separate package:

dotnet add package Orleans.EventSourcing.Kurrent.Reminders

Targets net8.0 and net10.0. Requires Microsoft Orleans 10.x and a KurrentDB / EventStoreDB server.

Quick start

Silo configuration

Pick whichever providers you need — they are configured separately and can use different options names if you want different ClientSettings per provider:

using KurrentDB.Client;
using Orleans.EventSourcing.Kurrent.Hosting;
using Orleans.EventSourcing.Kurrent.Reminders;

var settings = KurrentDBClientSettings.Create("esdb://localhost:2113?tls=false");

var builder = Host.CreateApplicationBuilder(args);
builder.UseOrleans(silo =>
{
    // Clustering provider - supports the Orleans membership table implemented via events
    silo.UseKurrentClustering(o => o.ClientSettings = settings);

    // Log-consistency provider — required for JournaledGrain<TView, TEntry>.
    silo.AddKurrentBasedLogConsistencyProviderAsDefault(o => o.ClientSettings = settings);

    // Grain state storage provider — required for Grain<TState> / [PersistentState] backed by Kurrent.
    silo.AddKurrentBasedGrainStorageProviderAsDefault(o => o.ClientSettings = settings);

    // Reminders provider — if you want to persist Orleans reminders in Kurrent.
    silo.AddKurrentReminderService(o => o.ClientSettings = settings);
});

Configure the Orleans client to use the Kurrent Clustering provider:

using KurrentDB.Client;
using Orleans.EventSourcing.Kurrent.Hosting;

var settings = KurrentDBClientSettings.Create("esdb://localhost:2113?tls=false");

var builder = Host.CreateApplicationBuilder(args);
builder.UseOrleansClient(client =>
{
    // Clustering provider - supports discovering silos by reading the cluster's event stream
    client.UseKurrentClustering(o => o.ClientSettings = settings);
});

The storage extensions also have non-default overloads that take a provider name so you can register multiple instances side-by-side.

To run KurrentDB locally (running insecure for testing only):

docker run --name kurrentdb-node -it -p 2113:2113 docker.kurrent.io/kurrent-latest/kurrentdb:latest --insecure

Using the clustering provider

The clustering provider event-sources the orleans IMembershipTable and by writing membership events to the cluster's event stream.

The stream contains MembershipUpdate events written by silos as they join or when their status or suspect list changes. Silos also update their IAmAlive value every 30 seconds by default; these are recorded as SiloAlive events.

In addition to removing defunct silos from the membership table, the interval configured for ClusteringMembershipOptions.DefunctSiloCleanupPeriod may be used to write a snapshot of the membership table and truncate the cluster's event stream. This prevents the stream length growing indefinitely and ensures recovery and discovery complete in a reasonable amount of time.

Configuring the clustering provider

The clustering provider can be configured using the delegate supplied to UseKurrentClustering

Options available in KurrentClusteringOptions include:

Name Type Default Description
ClientSettings KurrentDBClientSettings null The KurrentDB client settings used to connect to the KurrentDB server.
StreamPrefix string "Orleans.Cluster.Membership" The prefix for the stream name used by the clustering provider.
EventCountBeforeSnapshot int 5000 The number of events to write before taking a snapshot of the membership table.

Using the log-consistency provider (event-sourced grains)

Inherit from JournaledGrain<TView, TEntry>. Events are appended to a Kurrent stream per grain, and the view is rebuilt by replaying them.

Note: Grains replay all events from the stream on activation. For long-running grains, use [DiscardPriorEvents] on summary or snapshot events to bound replay cost.

public sealed class AccountGrain : JournaledGrain<AccountState, AccountEvent>, IAccountGrain
{
    public Task Deposit(decimal amount)
    {
        RaiseEvent(new AccountEvent.Deposited(amount));
        return ConfirmEvents();
    }
}

Truncating the event stream - Experimental

The provider supports truncating the event stream by using the DiscardPriorEvents attribute on an event type (OEK0002 — suppress with #pragma warning disable OEK0002 or <NoWarn>$(NoWarn);OEK0002</NoWarn>).

When an event with this attribute is committed, all prior events in the stream will be discarded.

This can be used to write a tombstone event e.g. AccountClosed or a summary event e.g. AccountBalance:

[DiscardPriorEvents]
[Alias("Account.Closed.V1")]
public record AccountClosed(DateTime ClosedAt) : AccountEvent;

public sealed class AccountGrain : JournaledGrain<AccountState, AccountEvent>, IAccountGrain
{
    public Task CloseAccount()
    {
        RaiseEvent(new AccountClosed(DateTime.UtcNow));
        return ConfirmEvents();
    }
}

Deleting the event stream

The log-consistency provider tombstones the event stream using JournaledGrain.ClearLogAsync.

Note: Once tombstoned a stream cannot be reused in Kurrent. To truncate the stream use the DiscardPriorEvents attribute instead.

The state-based provider soft deletes the stream using Grain<TState>.ClearStateAsync, which will delete the stream and allow it to be reused in Kurrent.

Subscribing to grain events (projections) - Experimental

Resolve IGrainEventProvider from the silo container (OEK0001 — suppress with #pragma warning disable OEK0001 or <NoWarn>$(NoWarn);OEK0001</NoWarn>) and call one of the SubscribeToGrainEvents* overloads. This reads the Kurrent $all stream filtered to streams written by the log-consistency provider, deserializes each event, and yields it together with the originating GrainId and version:

await foreach (var update in eventProvider.SubscribeToGrainEvents<IAccountGrain, AccountEvent>(
    subscriber: this.GetGrainId(),
    startingPosition: GlobalEventLogPosition.Start,
    cancellationToken: ct))
{
    switch (update)
    {
        case GrainEvent<AccountEvent> e:
            // project e.Event for e.EventGrainId at e.EventGrainVersion
            break;
        case Checkpoint cp:
            // persist cp.Position so the next subscription resumes from here
            break;
        case CaughtUp:
            // subscription is now live
            break;
        case FallenBehind:
            // subscription is lagging behind the live tail
            break;
    }
}

IGrainEventProvider only sees streams produced by the log-consistency provider. The state-storage provider's snapshot writes are not surfaced through this API.

Using the grain state storage provider

Use as a regular Orleans IGrainStorage — for example via [PersistentState]. Each WriteStateAsync appends one event to the grain's stream and Kurrent retains only the latest revision (stream metadata MaxCount = 1):

public sealed class SettingsGrain(
    [PersistentState(stateName: "settings")] IPersistentState<Settings> state)
    : Grain, ISettingsGrain
{
    public Task<Settings> Get() => Task.FromResult(state.State);

    public Task Set(Settings value)
    {
        state.State = value;
        return state.WriteStateAsync();
    }
}

Reminders provider

The reminders provider is shipped as a separate package:

dotnet add package Orleans.EventSourcing.Kurrent.Reminders

It records Orleans reminder upserts and removals as discrete events in KurrentDB via a JournaledGrain implementing IReminderTable.

Register it on the silo:

using Orleans.EventSourcing.Kurrent.Reminders;

builder.UseOrleans(silo =>
{
    silo.AddKurrentReminderService(o => o.ClientSettings = settings);
});

Reminders are persisted to a single stream. If you have high reminder churn, the stream will become large and it will take time to read the entire stream on startup.

Configuration

KurrentStorageOptions is configured per provider name. The same options type is used by both storage providers; configuring one named instance does not affect the other.

Property Description
ClientSettings KurrentDBClientSettings used to build the underlying KurrentClient.
GrainStorageSerializer Orleans' IGrainStorageSerializer used by the default event serializer.
StreamNameProvider IKurrentStreamNameProvider controlling stream-name layout. Defaults to KurrentStreamName.Default.

Custom event stream naming

The naming scheme is pluggable — see Custom event stream naming.

Both providers, and the projection subscription, route through IKurrentStreamNameProvider. The default produces:

  • {GrainType}-{Key} for log-consistency streams.
  • {GrainType}-{stateName}|{Key} for state-storage streams.

To change the scheme, implement IKurrentStreamNameProvider and assign it on the options for each provider you register:

silo.AddKurrentBasedLogConsistencyProviderAsDefault(o =>
{
    o.ClientSettings = settings;
    o.StreamNameProvider = new MyStreamNameProvider();
});

silo.AddKurrentBasedGrainStorageProviderAsDefault(o =>
{
    o.ClientSettings = settings;
    o.StreamNameProvider = new MyStreamNameProvider();
});

Event type names and [Alias]

The default serializers (DefaultEventSerializer<T> and EventEnvelopeSerializer<T>) write each event's CLR type into Kurrent's EventType field via Orleans' TypeConverter.Format(Type). That converter honours [Alias("...")] from Orleans.Metadata:

  • If the event type carries [Alias("MyAlias")], the Kurrent EventType is MyAlias.
  • Otherwise, the converter falls back to the Orleans assembly-qualified name format.

Implications:

  • [Alias] is the stable contract between your app and Kurrent. Renaming or moving an event type without an [Alias] will break replay because previously written EventType strings will no longer resolve. Always add [Alias] to event records you persist.
  • Aliases must be unique across the silo's serialization graph; this is an Orleans-wide constraint, not specific to this package.
[GenerateSerializer]
[Alias("Account.Deposited")]
public sealed record Deposited(decimal Amount);

If you need a different encoding (e.g., a versioned namespace scheme, JSON envelope with a discriminator, or interop with a non-Orleans producer), implement IEventSerializer<TLogEntry> and register it via DI before the provider, or supply your own through KurrentStorageOptions.

Accessing EventId and Event Metadata

If you need access to Kurrent's EventId or metadata fields in your grain, wrap your TLogEvent using EventEnvelope<TLogEvent> as your TLogEvent argument for JournaledGrain or IGrainEventProvider.

e.g. IGrainEventProvider.SubscribeToGrainEvents<EventEnvelope<T>> and JournaledGrain<TLogView, EventEnvelope<TLogEvent>>.

You can mix-and-match EventEnvelope<T> with T as needed within the same silo, as the serializer will handle both cases.

When you use EventEnvelope<T> a different IEventConverter is used which reads and writes the properties of the EventEnvelope into the appropriate Kurrent fields.

  • EventId property, which you can set if you want control over the eventId written to KurrentDB, or read from the event when replaying.
  • Metadata dictionary, which is persisted to KurrentDB and can be used to store additional information about the event that doesn't fit into the event payload. This can be useful for things like correlation ids, causation ids, or any other contextual information you want to associate with the event.

Notes & limitations

  • Read-optimisation snapshots are not yet supported; an event-sourced grain will replay all events on activation. Business snapshots are supported using events marked with [DiscardPriorEvents].

Versioning

Package versions are derived from git tags (vMAJOR.MINOR.PATCH[-prerelease]) using MinVer. Tagged commits publish stable versions; non-tag commits produce pre-release packages.

Experimental APIs

Some surface is annotated with [Experimental("OEK…")] and will produce a compiler diagnostic at every call site. The shape of these APIs may change in non-major releases. Acknowledge by suppressing the relevant diagnostic ID in your project (<NoWarn>$(NoWarn);OEK0001;OEK0002</NoWarn>) or with a localized #pragma warning disable.

Diagnostic API Purpose
OEK0001 IGrainEventProvider An interface available via dependency injection for grains hosting projections or side-effects to read grain events
OEK0002 DiscardPriorEventsAttribute An attribute to tell the storage provider to delete events prior to this event

License

License

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Orleans.EventSourcing.Kurrent:

Package Downloads
Orleans.EventSourcing.Kurrent.Reminders

An implementation of Orleans reminders using a JournaledGrain that implements IReminderTable.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.1.1-preview4 805 7/8/2026
10.1.1-preview3 64 7/7/2026
10.1.1-preview2 62 7/5/2026