DistributedLeasing.Azure.Blob 5.1.0

dotnet add package DistributedLeasing.Azure.Blob --version 5.1.0
                    
NuGet\Install-Package DistributedLeasing.Azure.Blob -Version 5.1.0
                    
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="DistributedLeasing.Azure.Blob" Version="5.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DistributedLeasing.Azure.Blob" Version="5.1.0" />
                    
Directory.Packages.props
<PackageReference Include="DistributedLeasing.Azure.Blob" />
                    
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 DistributedLeasing.Azure.Blob --version 5.1.0
                    
#r "nuget: DistributedLeasing.Azure.Blob, 5.1.0"
                    
#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 DistributedLeasing.Azure.Blob@5.1.0
                    
#: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=DistributedLeasing.Azure.Blob&version=5.1.0
                    
Install as a Cake Addin
#tool nuget:?package=DistributedLeasing.Azure.Blob&version=5.1.0
                    
Install as a Cake Tool

DistributedLeasing.Azure.Blob

NuGet Downloads

Azure Blob Storage distributed leasing provider for .NET

This package implements distributed leasing using native Azure Blob Storage lease capabilities. It leverages Azure's built-in pessimistic locking mechanism for reliable, cloud-native distributed coordination.

Features

Native Azure Blob Leases - Uses Azure's built-in lease mechanism (no polling)
Automatic Renewal - Background renewal keeps leases alive
Managed Identity Support - First-class Azure authentication integration
Metadata Storage - Store custom metadata with each lease
High Reliability - Azure-guaranteed consistency and durability
Simple Setup - Container auto-creation, minimal configuration

When to Use Azure Blob Leasing

Best For:

  • Leader election in Azure-hosted applications
  • Distributed lock coordination with moderate throughput
  • Long-running exclusive processes (minutes to hours)
  • Applications already using Azure Storage
  • Multi-region deployments with single-region coordination

Consider Alternatives When:

  • Need sub-second latency → Use Azure Redis
  • Need global distribution → Use Azure Cosmos DB
  • Need extremely high throughput (>1000 ops/sec per lease)

Installation

dotnet add package DistributedLeasing.Azure.Blob

This automatically includes DistributedLeasing.Abstractions with authentication and observability support.

Quick Start

Basic Usage with Managed Identity

using DistributedLeasing.Azure.Blob;
using Azure.Identity;

// Create provider with managed identity
var provider = new BlobLeaseProvider(new BlobLeaseProviderOptions
{
    ContainerUri = new Uri("https://mystorageaccount.blob.core.windows.net/leases"),
    Credential = new DefaultAzureCredential(),
    CreateContainerIfNotExists = true
});

// Create lease manager for a specific resource
var leaseManager = await provider.CreateLeaseManagerAsync("my-resource-lock");

// Acquire lease
var lease = await leaseManager.AcquireAsync(TimeSpan.FromSeconds(60));

if (lease != null)
{
    try
    {
        // Do work while holding the lease
        Console.WriteLine($"Lease acquired: {lease.LeaseId}");
        await DoExclusiveWorkAsync();
    }
    finally
    {
        // Always release when done
        await lease.ReleaseAsync();
    }
}
else
{
    Console.WriteLine("Could not acquire lease - another instance holds it");
}

Non-Blocking Acquisition

// Try to acquire without waiting
var lease = await leaseManager.TryAcquireAsync(TimeSpan.FromSeconds(60));

if (lease == null)
{
    // Lease is held by another instance - fail fast
    Console.WriteLine("Lease unavailable");
    return;
}

// Proceed with work

With Custom Metadata

var options = new BlobLeaseProviderOptions
{
    ContainerUri = new Uri("https://mystorageaccount.blob.core.windows.net/leases"),
    Credential = new DefaultAzureCredential(),
    Metadata = new Dictionary<string, string>
    {
        ["instance"] = Environment.MachineName,
        ["version"] = "1.2.3",
        ["region"] = "us-east-1"
    }
};

var provider = new BlobLeaseProvider(options);

Metadata is automatically prefixed with lease_ and stored with the blob. Useful for debugging and monitoring.

Configuration

Authentication Options

Option 1: Managed Identity (Recommended for Azure)

using Azure.Identity;

var options = new BlobLeaseProviderOptions
{
    ContainerUri = new Uri("https://mystorageaccount.blob.core.windows.net/leases"),
    Credential = new DefaultAzureCredential()
};

Option 2: Connection String (Development)

var options = new BlobLeaseProviderOptions
{
    ConnectionString = "DefaultEndpointsProtocol=https;AccountName=...",
    ContainerName = "leases"
};

Option 3: Specific Managed Identity

var options = new BlobLeaseProviderOptions
{
    ContainerUri = new Uri("https://mystorageaccount.blob.core.windows.net/leases"),
    Credential = new ManagedIdentityCredential("client-id-of-user-assigned-identity")
};

For comprehensive authentication configuration (including Service Principal, Workload Identity, etc.), see Authentication Guide.

Provider Options

public class BlobLeaseProviderOptions
{
    // Authentication (choose one)
    public Uri? ContainerUri { get; set; }
    public TokenCredential? Credential { get; set; }
    public string? ConnectionString { get; set; }
    public string? ContainerName { get; set; }

    // Container behavior
    public bool CreateContainerIfNotExists { get; set; } = true;

    // Lease configuration
    public LeaseOptions? LeaseOptions { get; set; }

    // Custom metadata
    public IDictionary<string, string>? Metadata { get; set; }
}

Lease Options

var leaseOptions = new LeaseOptions
{
    DefaultLeaseDuration = TimeSpan.FromSeconds(60),
    AutoRenew = true,
    AutoRenewInterval = TimeSpan.FromSeconds(40),
    MaxRetryAttempts = 3,
    RetryDelay = TimeSpan.FromSeconds(2)
};

var providerOptions = new BlobLeaseProviderOptions
{
    ContainerUri = containerUri,
    Credential = credential,
    LeaseOptions = leaseOptions
};

How Azure Blob Leasing Works

Blob Lease Mechanism

Azure Blob Storage provides native pessimistic locking through the lease API:

  1. Acquire: Application requests a lease on a blob for a specified duration (15-60 seconds)
  2. Lock: Azure grants an exclusive lease ID to the requester
  3. Renew: Lease holder periodically renews before expiration
  4. Release: Lease holder explicitly releases, or Azure auto-expires

Key Characteristics:

  • Pessimistic Lock: Only one lease holder at a time
  • Automatic Expiration: Azure guarantees lease expires if not renewed
  • No Polling: Lease state is authoritative server-side
  • High Reliability: Built on Azure Storage's consistency guarantees

Blob Naming Convention

Leases are stored as blobs with the naming pattern:

lease-{leaseKey}

Example:

  • Lease key: database-migration
  • Blob name: lease-database-migration

Metadata Storage

Each lease blob stores metadata automatically:

Metadata Key Description Example
leaseName Original lease key database-migration
createdAt Blob creation time 2025-12-25T12:00:00Z
lastModified Last metadata update 2025-12-25T12:05:30Z
lease_* Custom user metadata lease_instance: server-01

User-provided metadata is prefixed with lease_ to avoid conflicts.

Performance Characteristics

Latency

Operation Typical Latency Notes
Acquire (success) 50-150ms Single HTTP request to Azure
Acquire (failure) 50-150ms Fast fail if unavailable
Renew 50-150ms Background operation
Release 50-150ms Single HTTP request

Network Dependency: Latency varies by region and network conditions.

Throughput

  • Single Lease: ~100-200 operations/second (acquire/renew/release combined)
  • Multiple Leases: Scales linearly (each lease is independent blob)
  • Concurrent Acquisitions: Azure serializes requests per blob

Best Practice: For high-throughput scenarios (>1000 ops/sec), use Azure Redis instead.

Lease Duration Limits

  • Minimum: 15 seconds
  • Maximum: 60 seconds
  • Recommended: 30-60 seconds with renewal at 2/3 interval

Best Practices

1. Use Managed Identity in Production

// ✅ Good
var options = new BlobLeaseProviderOptions
{
    ContainerUri = new Uri("https://mystorageaccount.blob.core.windows.net/leases"),
    Credential = new DefaultAzureCredential()
};

// ❌ Avoid in production
var options = new BlobLeaseProviderOptions
{
    ConnectionString = "DefaultEndpointsProtocol=https;AccountName=..."
};

2. Always Release Leases

// ✅ Good
var lease = await leaseManager.AcquireAsync();
try
{
    await DoWorkAsync();
}
finally
{
    await lease.ReleaseAsync(); // Always release
}

// ❌ Risky - lease may not be released
var lease = await leaseManager.AcquireAsync();
await DoWorkAsync();

3. Handle Lease Loss

var cts = new CancellationTokenSource();

lease.LeaseLost += (sender, e) =>
{
    Console.WriteLine($"Lease lost: {e.Reason}");
    cts.Cancel(); // Stop work immediately
};

try
{
    await LongRunningWorkAsync(cts.Token);
}
finally
{
    await lease.ReleaseAsync();
}

4. Use Appropriate Lease Duration

// ✅ For short tasks (seconds to minutes)
var lease = await leaseManager.AcquireAsync(TimeSpan.FromSeconds(30));

// ✅ For longer tasks (minutes to hours)
// Auto-renewal keeps it alive
var lease = await leaseManager.AcquireAsync(TimeSpan.FromSeconds(60));

5. Add Metadata for Debugging

var options = new BlobLeaseProviderOptions
{
    ContainerUri = containerUri,
    Credential = credential,
    Metadata = new Dictionary<string, string>
    {
        ["instance"] = Environment.MachineName,
        ["process"] = Process.GetCurrentProcess().Id.ToString(),
        ["started"] = DateTime.UtcNow.ToString("o")
    }
};

Troubleshooting

"Container does not exist"

Problem: Container not created or wrong name.

Solution:

var options = new BlobLeaseProviderOptions
{
    ContainerUri = containerUri,
    Credential = credential,
    CreateContainerIfNotExists = true // Enable auto-creation
};

"Authentication failed"

Problem: Managed identity not configured or insufficient permissions.

Solution:

  1. Ensure managed identity is enabled on the Azure resource
  2. Assign "Storage Blob Data Contributor" role to the identity
  3. Verify container URI is correct

"Lease already exists" on acquire

Problem: Another instance holds the lease (expected behavior).

Solution: This is not an error - it's the distributed lock working correctly. Use TryAcquireAsync() for non-blocking behavior.

Frequent renewal failures

Problem: Network latency or lease duration too short.

Solution:

var leaseOptions = new LeaseOptions
{
    DefaultLeaseDuration = TimeSpan.FromSeconds(60), // Increase duration
    AutoRenewInterval = TimeSpan.FromSeconds(40)     // 2/3 of duration
};

Lease not released after application crash

Problem: Application terminated before releasing lease.

Solution: This is expected - Azure automatically expires the lease after the duration. Wait for expiration (max 60 seconds) before next acquisition.

Monitoring and Observability

Inspect Lease State with Azure CLI

# List all lease blobs
az storage blob list \
  --account-name mystorageaccount \
  --container-name leases \
  --output table

# Show lease metadata
az storage blob metadata show \
  --account-name mystorageaccount \
  --container-name leases \
  --name lease-my-resource-lock

# Check lease status
az storage blob show \
  --account-name mystorageaccount \
  --container-name leases \
  --name lease-my-resource-lock \
  --query "properties.lease"

Metrics and Health Checks

See DistributedLeasing.Abstractions README for:

  • OpenTelemetry metrics configuration
  • Health check setup
  • Distributed tracing integration

Samples

For comprehensive examples with real-world scenarios, see:

  • BlobLeaseSample - Complete distributed lock competition demo
    • Multiple instance competition
    • Automatic renewal examples
    • Metadata inspection
    • Troubleshooting scenarios

Architecture

┌─────────────────────────────────────────────────────┐
│ Your Application                                     │
│ ┌──────────────┐         ┌─────────────────┐       │
│ │ ILeaseProvider│────────▶│ BlobLeaseProvider│       │
│ └──────────────┘         └─────────────────┘       │
│         │                                            │
│         │ CreateLeaseManagerAsync("my-lock")        │
│         ▼                                            │
│ ┌──────────────┐         ┌──────────────────┐      │
│ │ILeaseManager │────────▶│ BlobLeaseManager │      │
│ └──────────────┘         └──────────────────┘      │
│         │                                            │
│         │ AcquireAsync()                            │
│         ▼                                            │
│ ┌──────────────┐         ┌──────────────┐          │
│ │   ILease     │────────▶│  BlobLease   │          │
│ └──────────────┘         └──────────────┘          │
└─────────────────────────────────────────────────────┘
                    │
                    │ Azure Blob Storage API
                    ▼
┌─────────────────────────────────────────────────────┐
│ Azure Blob Storage                                  │
│ ┌─────────────────────────────────────────────┐    │
│ │ Container: leases                            │    │
│ │ ┌─────────────────────────────────────┐     │    │
│ │ │ Blob: lease-my-lock                  │     │    │
│ │ │ • Lease ID: abc123...                │     │    │
│ │ │ • State: Leased                       │     │    │
│ │ │ • Duration: 60s                       │     │    │
│ │ │ • Metadata:                           │     │    │
│ │ │   - lease_instance: server-01         │     │    │
│ │ │   - lease_region: us-east             │     │    │
│ │ └─────────────────────────────────────┘     │    │
│ └─────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────┘

Framework Compatibility

  • .NET Standard 2.0 - Compatible with .NET Framework 4.6.1+, .NET Core 2.0+
  • .NET 8.0 - Long-term support release
  • .NET 10.0 - Latest release

Package Dependencies

Documentation

License

MIT License - see LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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 DistributedLeasing.Azure.Blob:

Package Downloads
DistributedLeasing.Extensions.DependencyInjection

Dependency injection extensions for the DistributedLeasing library. Provides service registration helpers for ASP.NET Core and other DI-enabled applications.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.1.0 189 12/25/2025
5.0.0 185 12/25/2025
4.0.0 175 12/24/2025
1.0.1 217 12/23/2025
1.0.0 220 12/23/2025

Minor version 5.1: Added Redis distributed locking sample with comprehensive documentation, enhanced setup-resources.sh script with --project argument supporting blob/cosmos/redis selection, interactive configuration wizard for all samples. Includes RedisLeaseSample demonstrating atomic SET NX operations, lock competition, and metadata inspection. No breaking API changes - safe to upgrade from 5.0.x.