Azure Storage grain persistence
The Microsoft.Orleans.Persistence.AzureStorage package contains providers for Azure Table Storage and Azure Blob Storage. Both implement optimistic concurrency using storage ETags.
Azure Table Storage
Section titled “Azure Table Storage”Table storage keeps one state record in an entity and splits serialized state across properties when needed. Azure Table Storage limits an entity to 1 MiB, so use Blob Storage or another provider for larger records.
Configure a named provider with AddAzureTableGrainStorage. Token credentials are preferred over secrets:
using Azure.Data.Tables;using Azure.Identity;siloBuilder.AddAzureTableGrainStorage( "profileStore", options => options.TableServiceClient = new TableServiceClient( new Uri("https://account.table.core.windows.net"), new DefaultAzureCredential()));Assign the storage account identity the data-plane permissions required to read and write table entities.
Azure Blob Storage
Section titled “Azure Blob Storage”Blob storage keeps each state record in a blob and is appropriate when state can exceed the Table Storage entity limit:
using Azure.Identity;using Azure.Storage.Blobs;siloBuilder.AddAzureBlobGrainStorage( "cartStore", options => options.BlobServiceClient = new BlobServiceClient( new Uri("https://account.blob.core.windows.net"), new DefaultAzureCredential()));Assign the storage account identity the data-plane permissions required to read and write blobs.
Connection strings
Section titled “Connection strings”Connection strings are useful for local emulators and constrained environments, but contain secrets and shouldn’t be committed:
var builder = Host.CreateApplicationBuilder();builder.UseOrleans(siloBuilder =>{ siloBuilder.AddAzureTableGrainStorage( name: "profileStore", configureOptions: options => { options.TableServiceClient = new TableServiceClient( "DefaultEndpointsProtocol=https;AccountName=data1;AccountKey=SOMETHING1"); }) .AddAzureBlobGrainStorage( name: "cartStore", configureOptions: options => { options.BlobServiceClient = new BlobServiceClient( "DefaultEndpointsProtocol=https;AccountName=data2;AccountKey=SOMETHING2"); });});
using var host = builder.Build();Use a secret store when a connection string is unavoidable.
Operational guidance
Section titled “Operational guidance”- Use separate provider names when records belong in different accounts, containers, or tables.
- Treat an InconsistentStateException as an optimistic-concurrency conflict, not a transient timeout.
- Configure storage redundancy and account failover according to the application’s durability requirements.
- Test record-size limits using serialized production-shaped state.
