Skip to content

Orleans grain directory

Grains have stable logical identities. They can activate (instantiate) and deactivate many times over the application’s life, but at most one activation of a grain exists at any point in time. Each time a grain activates, it might be placed on a different silo in the cluster. When a grain activates in the cluster, it registers itself in the grain directory. This ensures subsequent invocations of that grain are delivered to that activation and prevents the creation of other activations (instances) of that grain. The grain directory is responsible for maintaining a mapping between a grain identity and the location (which silo) of its current activation.

Applies to: Orleans 10.0, Orleans 9.0

Orleans provides several grain directory implementations:

DirectoryPackageDescription
Distributed In-Cluster (default)Built-inEventually consistent, partitioned across silos using a distributed hash table. Allows occasional duplicate activations during cluster instability.
Strongly-Consistent In-ClusterBuilt-inStrongly consistent distributed hash table with versioned range locks. Prevents duplicate activations but requires more coordination.
ADO.NETMicrosoft.Orleans.GrainDirectory.AdoNetDatabase-backed directory supporting SQL Server, PostgreSQL, MySQL, and Oracle.
Azure Table StorageMicrosoft.Orleans.GrainDirectory.AzureStorageAzure Table-backed directory for persistent grain locations.
RedisMicrosoft.Orleans.GrainDirectory.RedisRedis-backed directory for high-performance persistent lookups.

Applies to: Orleans 8.0, Orleans 7.0, Orleans 3.x

By default, Orleans uses a built-in distributed in-cluster directory. This directory is eventually consistent and partitioned across all silos in the cluster in the form of a distributed hash table.

Starting with version 3.2.0, Orleans also supports pluggable grain directory implementations.

Two such plugins are included in the 3.2.0 release:

You can configure which grain directory implementation to use on a per-grain type basis, and you can even inject your implementation.

We recommend always starting with the default directory (the built-in distributed in-cluster directory). Although it’s eventually consistent and allows occasional duplicate activations when the cluster is unstable, the built-in directory is self-sufficient, has no external dependencies, requires no configuration, and has been used successfully in production since the beginning.

When you have some experience with Orleans and have a use case requiring a stronger single-activation guarantee, or if you want to minimize the number of grains deactivated when a silo shuts down, consider using a storage-based grain directory implementation, such as the Redis implementation. Try using it for one or a few grain types first, starting with those that are long-lived, have significant state, or have an expensive initialization process.

Applies to: Orleans 10.0, Orleans 9.0

Orleans also provides a strongly-consistent grain directory using a distributed hash table with virtual nodes (similar to Amazon Dynamo and Apache Cassandra). Unlike the default eventually-consistent directory, this implementation prevents duplicate grain activations even during cluster instability.

  • Strong consistency: Uses versioned range locks during view changes to ensure consistency
  • Virtual nodes: Each silo manages 30 virtual nodes (partitions) on the hash ring for better distribution
  • Automatic recovery: Recovers automatically when silos crash without completing handoff
  • Two-phase operation: Operates in normal phase and view-change phase for safe membership transitions

To use the strongly-consistent directory, explicitly add it using AddDistributedGrainDirectory:

// Use as the default grain directory
builder.AddDistributedGrainDirectory();
// Or add as a named directory
builder.AddDistributedGrainDirectory("MyDistributedDirectory");

Use the strongly-consistent directory when you need to prevent duplicate grain activations during cluster membership changes. The default eventually-consistent directory is suitable for most scenarios where occasional duplicate activations are acceptable.

Consider external storage-backed directories (Redis, Azure Table, ADO.NET) when:

  • You need grain registrations to persist across full cluster restarts
  • You have very large clusters where memory usage is a concern

The ADO.NET-based grain directory stores grain locations in a relational database. This provides persistent grain location storage that survives cluster restarts.

  • SQL Server
  • PostgreSQL
  • MySQL / MariaDB
  • Oracle

Install the NuGet package:

Terminal window
dotnet add package Microsoft.Orleans.GrainDirectory.AdoNet

Configure the ADO.NET grain directory as the default or as a named directory:

// Use as the default grain directory
builder.UseAdoNetGrainDirectoryAsDefault(options =>
{
options.Invariant = "System.Data.SqlClient"; // or "Npgsql", "MySql.Data.MySqlClient"
options.ConnectionString = "Server=localhost;Database=Orleans;...";
});
// Or add as a named directory
builder.AddAdoNetGrainDirectory("MyAdoNetDirectory", options =>
{
options.Invariant = "Npgsql";
options.ConnectionString = "Host=localhost;Database=Orleans;...";
});
OptionTypeDescription
InvariantstringRequired. The ADO.NET provider invariant name (e.g., System.Data.SqlClient, Npgsql, MySql.Data.MySqlClient).
ConnectionStringstringRequired. The database connection string. This value is redacted in logs.

Before using the ADO.NET grain directory, you must create the required database tables. Run the appropriate SQL script for your database:

By default, you don’t need to do anything; Orleans automatically uses the in-cluster grain directory and partitions it across the cluster. If you want to use a non-default grain directory configuration, you need to specify the name of the directory plugin to use. You can do this via an attribute on the grain class and by configuring the directory plugin with that name using dependency injection during silo configuration.

Specify the grain directory plugin name using the GrainDirectoryAttribute:

[GrainDirectory(GrainDirectoryName = "my-grain-directory")]
public class MyGrain : Grain, IMyGrain
{
// ...
}

Here’s how you configure the Redis grain directory implementation:

siloBuilder.AddRedisGrainDirectory(
"my-grain-directory",
options => options.ConfigurationOptions = redisConfiguration);

Configure the Azure grain directory like this:

siloBuilder.AddAzureTableGrainDirectory(
"my-grain-directory",
options => options.ConnectionString = azureConnectionString);

You can configure multiple directories with different names for use with different grain classes:

siloBuilder
.AddRedisGrainDirectory(
"redis-directory-1",
options => options.ConfigurationOptions = redisConfiguration1)
.AddRedisGrainDirectory(
"redis-directory-2",
options => options.ConfigurationOptions = redisConfiguration2)
.AddAzureTableGrainDirectory(
"azure-directory",
options => options.ConnectionString = azureConnectionString);