Table of Contents

Configuration

This page documents Orleans.Lattice.Storage.AzureTable 9.9.0, in the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at configuration.md, and llms.txt lists every page.

This document covers AzureTableWalStorageOptions, the public configuration surface for Orleans.Lattice.Storage.AzureTable. Register the provider with AddAzureTableWalStorage; see API Reference for public types and Architecture for behavioural details.

Registering the provider

using Orleans.Lattice.Storage.AzureTable;

siloBuilder.AddAzureTableWalStorage(o =>
{
    o.ConnectionString = "UseDevelopmentStorage=true";
    o.TableName = "OrleansLatticeWal";
});

Each registration must configure exactly one authentication mode. The connection-string form above is one option; the three registrations below show the other mutually exclusive alternatives - pick whichever one matches your deployment, not more than one:

using Azure.Core;
using Azure.Data.Tables;
using Orleans.Lattice.Storage.AzureTable;

TokenCredential tokenCredential = null!;
TableSharedKeyCredential sharedKeyCredential = null!;
TableServiceClient serviceClient = null!;

siloBuilder.AddAzureTableWalStorage(o =>
{
    o.ServiceUri = new Uri("https://account.table.core.windows.net");
    o.TokenCredential = tokenCredential;
});

siloBuilder.AddAzureTableWalStorage(o =>
{
    o.ServiceUri = new Uri("https://account.table.core.windows.net");
    o.SharedKeyCredential = sharedKeyCredential;
});

siloBuilder.AddAzureTableWalStorage(o =>
{
    o.ServiceClient = serviceClient;
});

Options Reference - AzureTableWalStorageOptions

Authentication and client

Option Type Default
ConnectionString string? null
ServiceUri Uri? null
TokenCredential TokenCredential? null
SharedKeyCredential TableSharedKeyCredential? null
ServiceClient TableServiceClient? null
TableName string "OrleansLatticeWal"
ConfigureClientOptions Action<TableClientOptions>? null

Retry options

Option Type Default
RetryMaxAttempts int? null
RetryDelay TimeSpan? null
RetryMaxDelay TimeSpan? null
RetryNetworkTimeout TimeSpan? 10 s
RetryMode RetryMode? null

Commit pipeline options

Option Type Default
PipelinePhaseTwoCommits bool true
EliminateCandidateRowOnHotPath bool true
PipelinedPhaseTwoFaultHandler Action<Exception>? null
PhaseTwoCoalescingWindow TimeSpan 5 ms
PhaseTwoCommitTimeout TimeSpan? 12 seconds
PhaseOneTransientRetryMaxAttempts int 2
PhaseOneTransientRetryBaseDelay TimeSpan 25 ms

Saturation options

Option Type Default
HonorSaturationSignal bool true
SaturationShortCircuitCooldown TimeSpan 2 seconds

Compression options

Option Type Default
Compression LatticeCompression Zstd
CompressionMinPayloadBytes int 256

Every non-null default above is also published as a public member of AzureTableWalStorageOptions: the constants DefaultTableName, DefaultPipelinePhaseTwoCommits, DefaultEliminateCandidateRowOnHotPath, DefaultHonorSaturationSignal, DefaultPhaseOneTransientRetryMaxAttempts, DefaultCompression, and DefaultCompressionMinPayloadBytes, and the static readonly TimeSpan fields DefaultRetryNetworkTimeout, DefaultPhaseTwoCoalescingWindow, DefaultPhaseTwoCommitTimeout, DefaultSaturationShortCircuitCooldown, and DefaultPhaseOneTransientRetryBaseDelay. DefaultPhaseOneTransientRetryMaxDelay (250 ms) is not an option's default but the fixed cap on the phase-1 retry backoff.

Option guidance

ConnectionString

Storage account connection string. Use this for Azurite, development, or deployments that manage secrets outside the Azure identity stack. Mutually exclusive with ServiceUri, credentials, and ServiceClient.

ServiceUri

Azure Table service endpoint. Pair it with exactly one credential: TokenCredential or SharedKeyCredential.

TokenCredential

Azure identity credential used with ServiceUri. This is the preferred production shape for managed identity or workload identity.

SharedKeyCredential

Shared-key credential used with ServiceUri. Mutually exclusive with TokenCredential.

ServiceClient

Pre-built TableServiceClient supplied by the host. When set, the provider uses it verbatim. ConfigureClientOptions, retry knobs, and provider-attached Azure SDK policies do not modify the supplied client; the host owns its options, pipeline, and lifetime.

TableName

Azure Table name used for WAL storage. Defaults to AzureTableWalStorageOptions.DefaultTableName. The table is created on first use. Use distinct table names when sharing one storage account across independent Lattice deployments.

ConfigureClientOptions

Callback used only when the provider constructs a TableServiceClient. Retry knobs are applied before this callback, so callback changes have final say. Add custom policies with AddPolicy rather than replacing the whole retry setup unless you intentionally want to own it.

RetryMaxAttempts

Overrides the Azure SDK retry count after the initial attempt. null leaves the SDK default. 0 disables retries. Must be non-negative.

RetryDelay

Overrides the Azure SDK base retry delay. null leaves the SDK default. Must be non-negative and must not exceed RetryMaxDelay when both are set.

RetryMaxDelay

Overrides the Azure SDK maximum retry delay. null leaves the SDK default. Must be non-negative and at least RetryDelay when both are set.

RetryNetworkTimeout

Overrides the Azure SDK per-attempt network timeout. Defaults to 10 s - a finite bound below the WAL flush budget so a stuck request surfaces a fault into the WAL shard's failure handler (which releases and recovers the slot) instead of being abandoned while the transport zombies on for the SDK's unbounded ~100 s default. Set to null to restore the SDK default; must be positive when set. Prevents one stuck request from occupying a WAL slot - and, under a sustained storage brown-out, accumulating into hundreds of zombie attempts that self-sustain the brown-out.

RetryMode

Overrides the Azure SDK retry mode. null leaves the SDK default, usually exponential backoff.

using Azure.Core;
using Orleans.Lattice.Storage.AzureTable;

siloBuilder.AddAzureTableWalStorage(o =>
{
    o.ConnectionString = "UseDevelopmentStorage=true";
    o.RetryMaxAttempts = 2;
    o.RetryDelay = TimeSpan.FromMilliseconds(50);
    o.RetryMaxDelay = TimeSpan.FromSeconds(1);
    o.RetryNetworkTimeout = TimeSpan.FromSeconds(5);
    o.RetryMode = RetryMode.Exponential;
});

PipelinePhaseTwoCommits

When true, the provider can return from an append after the durable entry write and after observing the previous pending completion for the same shard. Each completion commit still writes its pending batches in ascending offset order, and failures remain sticky to a later append or the configured fault handler. Set to false when you want every append to wait for its own completion before returning.

Because the read path is derived from commit metadata, this option introduces a bounded read visibility lag: the trailing batch on a shard is durable when the append returns but is not readable until its completion lands, so a read can omit it. The reported highest offset does not lag - GetHighestOffsetAsync folds the already-durable batches the shard's completion worker has accepted over the stored tail, so it never reports an offset lower than one a completed append returned. Callers that need read-after-write should await the provider's phase-two flush barrier rather than sleeping or polling; see Architecture. Set the option to false if you would rather pay the latency on every append than take a barrier where you need one.

EliminateCandidateRowOnHotPath

When true, the provider removes an extra recovery-marker write from the normal append path. Recovery still detects interrupted batches using stored batch metadata and the committed shard tail. Upgrade from false to true is safe because both recovery shapes are recognized. Before moving from true back to false, drain pending appends and let reconciliation complete on a true deployment.

PipelinedPhaseTwoFaultHandler

Optional observer invoked once for every pipelined completion that faults, whether or not a successor append later observes the same fault, so a fault on a shard that goes idle before a successor append arrives still reaches the application. Because one fault can be reported both here and to that successor, the delegate should be idempotent and observability-only. Exceptions thrown by the delegate are ignored. It is never invoked when PipelinePhaseTwoCommits is false, because every append then observes its own completion.

using Orleans.Lattice.Storage.AzureTable;

siloBuilder.AddAzureTableWalStorage(o =>
{
    o.ConnectionString = "UseDevelopmentStorage=true";
    o.PipelinedPhaseTwoFaultHandler = static ex => _ = ex.Message;
});

PhaseTwoCoalescingWindow

Maximum wait after the first pending completion arrives so more completions can be coalesced into the same Azure Table transaction. Default is 5 ms. Must be non-negative. Use TimeSpan.Zero to commit as soon as work is available.

PhaseTwoCommitTimeout

Per-commit deadline for the ordered completion transaction. Default is 12 seconds. Set to null to make completion unbounded, or set a positive TimeSpan tuned above the worst commit latency a browned-out account produces and below the silo-level timeout.

The deadline bounds how long the shard waits, not the transaction: on a timeout the waiting batches fail fast, but the abandoned transaction keeps running to a real outcome, because the service may already hold it and can still apply it. The shard's post-failure resync waits for it before reading the log, so the writer never resumes beneath rows that transaction then writes. A transaction still running 60 seconds after it was abandoned is cancelled so the wait stays bounded.

The 12-second default comes from multi-silo real-Azure measurement: one storage account under about 36k keys/s had a 6-9 second latency brown-out in which completion transactions took up to 6 seconds. The former 3-second default tripped on every shard at once and turned that brown-out into a failure storm; with no deadline the same run failed no keys. 12 seconds leaves 2x headroom over that worst case while staying below the 15-second WAL flush timeout and the silo request timeout, so a genuinely wedged shard is still broken.

using Orleans.Lattice.Storage.AzureTable;

var options = new AzureTableWalStorageOptions
{
    ConnectionString = "UseDevelopmentStorage=true",
    PhaseTwoCommitTimeout = TimeSpan.FromSeconds(30),
};

PhaseOneTransientRetryMaxAttempts

Number of additional in-place retries the provider issues when a phase-1 batch commit faults transiently. Each retry resubmits the byte-identical batch at the same offsets, so an already-durable prior attempt is resolved as an idempotent-replay success and a genuine offset collision still surfaces immediately without retry. Default is 2 (three attempts total); each retry increments orleans.lattice.provider.phase1.transient_retries. Set to 0 to surface every transient fault immediately. This knob is not validated: a negative value is treated as 0.

PhaseOneTransientRetryBaseDelay

Base delay for the jittered backoff between phase-1 transient retries. The wait before the n-th retry (1-based) is a random value in [0, BaseDelay * n), capped at 250 ms (AzureTableWalStorageOptions.DefaultPhaseOneTransientRetryMaxDelay), so multiple hot shards do not retry in lockstep. Default is 25 ms. Set to TimeSpan.Zero to retry without delay. This knob is not validated: a negative value also retries without delay.

HonorSaturationSignal

When true, and when IWalSaturationSignal is registered, the provider attaches SaturationAwareRetryPolicy to clients it constructs. The first attempt reaches the network; retry attempts can short-circuit while aggregate WAL state is saturated. Set to false to leave Azure SDK retries unguarded.

SaturationShortCircuitCooldown

Sticky window after the last saturated observation during which retry attempts continue to short-circuit. Default is 2 seconds. Must be non-negative. Set to TimeSpan.Zero to consult only the present aggregate state.

using Orleans.Lattice.Storage.AzureTable;

var optionsWithCooldownOverride = new AzureTableWalStorageOptions
{
    ConnectionString = "UseDevelopmentStorage=true",
    HonorSaturationSignal = true,
    SaturationShortCircuitCooldown = TimeSpan.FromSeconds(5),
};

Compression

Stored WAL payload compression algorithm. Default is LatticeCompression.Zstd; set LatticeCompression.None to store payloads verbatim. Rows are self-describing, so changing this option affects newly written rows while older rows continue to decode with their recorded tags.

Compression also decides whether a large entry fits. Each entry is stored in one binary property, which the Azure Table service limits to 64 KiB, measured on the stored bytes. With LatticeCompression.None, or for a payload compression cannot shrink, an entry fails as soon as its encoded record exceeds 64 KiB; a compressible one fits while its compressed form stays under the limit. See Architecture.

CompressionMinPayloadBytes

Minimum encoded payload size at which compression is attempted. Default is 256 bytes. Must be non-negative. Ignored when Compression is LatticeCompression.None.

using Orleans.Lattice;
using Orleans.Lattice.Storage.AzureTable;

siloBuilder.AddAzureTableWalStorage(o =>
{
    o.ConnectionString = "UseDevelopmentStorage=true";
    o.Compression = LatticeCompression.None;
    o.CompressionMinPayloadBytes = 0;
});

Validation

When the provider is constructed it rejects a negative CompressionMinPayloadBytes (ArgumentOutOfRangeException), a registered ILatticeCompressor that claims LatticeCompression.None or shares an algorithm tag with another registered compressor (ArgumentException), and a Compression algorithm other than None for which no ILatticeCompressor is registered (InvalidOperationException).

At first use - when it builds its Azure SDK client and creates the table - the provider validates the following, throwing InvalidOperationException on a violation:

  • Exactly one authentication mode is configured.
  • TableName is non-empty.
  • Credential modes are mutually exclusive.
  • Retry values are within valid ranges.
  • RetryDelay does not exceed RetryMaxDelay when both are set.
  • PhaseTwoCoalescingWindow and SaturationShortCircuitCooldown are non-negative.
  • PhaseTwoCommitTimeout is positive when set.
  • CompressionMinPayloadBytes is non-negative.

See also