Azure Storage Provider

January 23, 2026 · View on GitHub

The Azure Storage provider uses Azure Storage queues, tables, and blobs to persist orchestration state. It's a self-managed option suitable for existing Azure Storage deployments.

When to Use Azure Storage

Good for:

  • Existing Azure Storage infrastructure
  • Cost-sensitive workloads with low-to-moderate throughput
  • Internal Azure services in Ring-1 or lower

⚠️ Consider Durable Task Scheduler instead for:

  • New projects
  • Enterprise support requirements
  • High-throughput scenarios
  • Zero infrastructure management
  • Internal Azure services in Ring-2 or higher

Installation

dotnet add package Microsoft.Azure.DurableTask.AzureStorage

Configuration

Basic Setup

using DurableTask.AzureStorage;
using DurableTask.Core;
using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
    builder.SetMinimumLevel(LogLevel.Information);
});

var settings = new AzureStorageOrchestrationServiceSettings
{
    StorageConnectionString = "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;",
    TaskHubName = "MyTaskHub",
    LoggerFactory = loggerFactory
};

var service = new AzureStorageOrchestrationService(settings);
await service.CreateIfNotExistsAsync();

var worker = new TaskHubWorker(service, loggerFactory);
var client = new TaskHubClient(service, loggerFactory: loggerFactory);

Using Managed Identity

using Azure.Identity;
using DurableTask.AzureStorage;
using DurableTask.Core;
using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
    builder.SetMinimumLevel(LogLevel.Information);
});

// Uses DefaultAzureCredential
var credential = new DefaultAzureCredential();

var settings = new AzureStorageOrchestrationServiceSettings
{
    TaskHubName = "MyTaskHub",
    StorageAccountClientProvider = new StorageAccountClientProvider("mystorageaccount", credential),
    LoggerFactory = loggerFactory
};

var service = new AzureStorageOrchestrationService(settings);

Tip

For complete runnable examples using managed identity, see the Managed Identity Samples.

Configuration Options

Core Settings

SettingDescriptionDefault
TaskHubNameName of the task hub (alphanumeric, 3-45 chars)Required
StorageConnectionStringAzure Storage connection stringRequired*
StorageAccountNameStorage account name (for managed identity)Required*

*Either StorageConnectionString or StorageAccountName with credentials is required.

Performance Settings

SettingDescriptionDefault
PartitionCountNumber of control queue partitions (1-16)4
ControlQueueBufferThresholdMax messages prefetched and buffered per partition64
MaxConcurrentTaskOrchestrationWorkItemsMax concurrent orchestrations100
MaxConcurrentTaskActivityWorkItemsMax concurrent activities10

Partition Management

SettingDescriptionDefault
UseTablePartitionManagementUse table-based partition management (recommended)true
UseLegacyPartitionManagementUse legacy blob-based partition managementfalse
LeaseRenewIntervalInterval for renewing partition leases10 seconds
LeaseIntervalLease duration before expiration30 seconds
LeaseAcquireIntervalInterval for checking partition balance10 seconds

Note

Table-based partition management (UseTablePartitionManagement = true) is the default and recommended option. It provides better reliability for partition distribution and uses a {taskhub}Partitions table instead of blob leases. It's also significantly less expensive in terms of Azure Storage operations.

Example Configuration

var settings = new AzureStorageOrchestrationServiceSettings
{
    StorageConnectionString = connectionString,
    TaskHubName = "MyTaskHub",
    
    // Performance tuning
    PartitionCount = 8,
    ControlQueueBufferThreshold = 128,
    MaxConcurrentTaskOrchestrationWorkItems = 200,
    MaxConcurrentTaskActivityWorkItems = 200,
    
    // Lease settings
    LeaseInterval = TimeSpan.FromSeconds(30),
    LeaseRenewInterval = TimeSpan.FromSeconds(10)
};

Architecture

Storage Resources

The Azure Storage provider creates these resources:

Resource TypeName PatternPurpose
Control Queues{taskhub}-control-{0..N}Orchestration messages
Work Item Queue{taskhub}-workitemsActivity messages
History Table{taskhub}HistoryOrchestration history
Instances Table{taskhub}InstancesInstance metadata
Partitions Table{taskhub}PartitionsPartition leases (table manager)
Lease Blobs{taskhub}-leases/Partition leases (blob manager)

Partitioning

The Azure Storage provider uses partitions to distribute orchestration workloads across workers. Each partition corresponds to exactly one control queue.

How Partitioning Works

  • Orchestrations and entities are assigned to partitions by hashing the instance ID
  • Instance IDs are random GUIDs by default, ensuring even distribution across partitions
  • A single orchestration instance is always processed by one partition (and therefore one worker) at a time
  • The PartitionCount setting (1–16, default 4) determines how many control queues are created

Queue Architecture

The task hub uses two types of queues:

Queue TypeCountPurposeProcessing
Control queuesPartitionCountOrchestration lifecycle messagesPartitioned — each queue owned by one worker
Work item queue1Activity function messagesShared — all workers compete for messages
┌──────────────────────────────────────────────────────────────────────┐
│                            Task Hub                                  │
│                                                                      │
│  CONTROL QUEUES (partitioned)           WORK ITEM QUEUE (shared)     │
│  ┌──────────────────────────────────┐   ┌─────────────────────────┐  │
│  │ control-00 │ control-01 │ ...   │   │      workitems          │  │
│  │ (Worker A) │ (Worker B) │       │   │                         │  │
│  │            │            │       │   │  All workers compete    │  │
│  │ • Start    │ • Start    │       │   │  for activity messages  │  │
│  │ • Timer    │ • Timer    │       │   │                         │  │
│  │ • Activity │ • Activity │       │   └─────────────────────────┘  │
│  │   complete │   complete │       │              ▲                 │
│  │ • External │ • External │       │              │                 │
│  │   event    │   event    │       │   Activities scheduled by      │
│  └────────────┴────────────┴───────┘   orchestrators go here        │
│         │            │                                              │
│         ▼            ▼                                              │
│  ┌──────────────────────────────────┐                               │
│  │      ORCHESTRATION INSTANCES     │                               │
│  │  Hash(InstanceID) → Partition    │                               │
│  └──────────────────────────────────┘                               │
└──────────────────────────────────────────────────────────────────────┘

Control Queue Messages

Control queues contain orchestration lifecycle messages:

  • ExecutionStarted — New orchestration started
  • TaskCompleted — Activity function completed
  • TimerFired — Durable timer expired
  • EventRaised — External event received
  • SubOrchestrationCompleted — Child orchestration completed

When messages are dequeued, up to 32 messages are fetched in a single poll. Messages for the same instance are batched together for efficient processing.

Work Item Queue

The work item queue is a simple, non-partitioned queue for activity function messages:

  • All workers compete to dequeue activity messages
  • Activities are stateless — any worker can execute any activity
  • Activities can scale out infinitely (limited only by worker count)

Partition Count Guidance

WorkloadRecommended PartitionCount
Development/testing1–2
Low-to-moderate throughput4 (default)
High throughput8–16

Important

Partition count cannot be changed after task hub creation. Set it high enough to accommodate future scale-out needs. The maximum number of workers that can process orchestrations concurrently equals the partition count. Note that higher partition counts increase Azure Storage costs due to more queue and table operations.

Lease Management

Workers compete for partition ownership using one of two partition managers:

Table Partition Manager (Default)

When UseTablePartitionManagement = true (default):

  • Partition leases are stored in the {taskhub}Partitions table
  • Uses Azure Table ETags for concurrency control
  • Provides better reliability due to transactional updates

Blob Partition Manager (Legacy)

When UseTablePartitionManagement = false:

  • Partition leases are stored as blobs in {taskhub}-leases/
  • Uses Azure Blob leases for concurrency control
  • Available in "safe" (UseLegacyPartitionManagement = false) and "legacy" (UseLegacyPartitionManagement = true) variants

Partition lifecycle

  1. Workers acquire leases to claim partition ownership
  2. Leases are renewed at LeaseRenewInterval (default 10s)
  3. Leases expire after LeaseInterval (default 30s) if not renewed
  4. Partitions are automatically balanced across workers

Message Processing

  1. Prefetching: Messages are prefetched from control queues in batches
  2. Batching: Messages for the same instance are grouped together
  3. History fetch: Orchestration history is loaded from Table Storage
  4. Processing: Orchestration code runs with the loaded history
  5. Checkpoint: New history and messages are appended

Checkpoint Order

Checkpoints are written in this order to ensure that no data is lost if there is a failure:

  1. New messages → Storage queues
  2. New history → Table storage
  3. Delete processed messages

Because the checkpoints aren't atomic, duplicates may occur. The replay model handles history duplicates gracefully. Message duplicates may result in activities being executed multiple times if an unexpected failure occurs.

Scaling

Horizontal Scaling

Multiple workers can connect to the same task hub:

// Worker 1, 2, 3... all connect to same task hub
var service = new AzureStorageOrchestrationService(settings);
var worker = new TaskHubWorker(service, loggerFactory);
await worker.StartAsync();

Partitions are automatically distributed across workers.

Partition Count

For high-throughput scenarios, increase partition count:

var settings = new AzureStorageOrchestrationServiceSettings
{
    PartitionCount = 16  // More partitions = more parallelism
};

Warning

Partition count cannot be changed after task hub creation.

Operations

Create Task Hub

await service.CreateIfNotExistsAsync();

Delete Task Hub

await service.DeleteAsync();

Purge History

await service.PurgeOrchestrationHistoryAsync(
    DateTime.UtcNow.AddDays(-30),  // Older than 30 days
    OrchestrationStateTimeRangeFilterType.OrchestrationCompletedTimeFilter);

Warning

Purging history is very expensive and may take hours for large task hubs. It's recommended to purge history frequently and in smaller time ranges.

Monitoring

Azure Storage Metrics

Monitor these metrics in Azure portal:

  • Queue message count
  • Table transactions
  • Blob lease operations

Logging

The Azure Storage provider supports structured logging via Microsoft.Extensions.Logging.

Enabling Logging

using Microsoft.Extensions.Logging;

// Create a logger factory
ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
    builder.AddFilter("DurableTask.AzureStorage", LogLevel.Information);
    builder.AddFilter("DurableTask.Core", LogLevel.Information);
});

// Pass logger factory to settings
var settings = new AzureStorageOrchestrationServiceSettings
{
    StorageConnectionString = connectionString,
    TaskHubName = "MyTaskHub",
    LoggerFactory = loggerFactory
};

var service = new AzureStorageOrchestrationService(settings);

Log Categories

CategoryDescription
DurableTask.AzureStorageAzure Storage-specific operations (messages, queues, tables)
DurableTask.CoreCore framework operations (orchestrations, activities, dispatchers)

Example Log Events

  • SendingMessage / ReceivedMessage — Queue message operations
  • FetchedInstanceHistory — History table reads
  • PoisonMessageDetected — Unprocessable messages
  • PartitionManagerInfo / PartitionManagerWarning — Partition management

ETW Event Source

Events are also published to Event Tracing for Windows (ETW) via the DurableTask-AzureStorage event source (GUID: {4C4AD4A2-F396-5E18-01B6-618C12A10433}).

Next Steps