GM.DistributedLock.Redis 1.0.0

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

<p align="center"> <img src="https://raw.githubusercontent.com/gmetskhvarishvili/GM.DistributedLock/master/icon.png" alt="GM.DistributedLock" width="140" height="140" /> </p>

GM.DistributedLock

CI NuGet License: MIT

A small, provider-agnostic distributed lock for .NET. Depend on one interface — IDistributedLock — with TryAcquire (single shot) and Acquire (blocking with retry), each returning an IAsyncDisposable handle that releases on dispose and is owner-safe (it never releases a lock that expired and was re-acquired by someone else). Ships a single-process in-memory implementation; add a real backend with GM.DistributedLock.Redis. Targets .NET 10.

Packages

The two packages version and release together (lockstep):

Package What it gives you
GM.DistributedLock IDistributedLock, ILockHandle, and a single-process in-memory implementation (AddGMDistributedLock()) for dev and tests.
GM.DistributedLock.Redis A real cross-process lock over StackExchange.Redis — SET NX PX to acquire, owner-checked Lua to release (AddGMRedisDistributedLock()).
dotnet add package GM.DistributedLock          # in-process
dotnet add package GM.DistributedLock.Redis     # + Redis backend

Quick start

Register

using GM.DistributedLock;         // in-process
builder.Services.AddGMDistributedLock();

// or, cross-process:
using GM.DistributedLock.Redis;
builder.Services.AddGMRedisDistributedLock(o => o.ConnectionString = "localhost:6379");
// or bind from configuration: AddGMRedisDistributedLock(builder.Configuration, "Redis");

Use it

Inject IDistributedLock — the same code works with either backend:

public class PayoutJob(IDistributedLock locks)
{
    public async Task RunAsync(Guid accountId)
    {
        // Block up to 30s, retrying every 200ms, holding the lock for at most 60s.
        await using var handle = await locks.AcquireAsync(
            resource: $"payout:{accountId}",
            expiry: TimeSpan.FromSeconds(60),
            wait: TimeSpan.FromSeconds(30),
            retryInterval: TimeSpan.FromMilliseconds(200));

        // ... critical section: only one worker runs this per account ...
    }   // lock released here
}

Prefer a non-blocking attempt? Use TryAcquireAsync, which returns null when the lock is held:

await using var handle = await locks.TryAcquireAsync($"report:{id}", TimeSpan.FromMinutes(5));
if (handle is null) return; // someone else is already generating it

The interface

Task<ILockHandle?> TryAcquireAsync(string resource, TimeSpan expiry, CancellationToken ct = default);
Task<ILockHandle>  AcquireAsync(string resource, TimeSpan expiry, TimeSpan wait, TimeSpan retryInterval, CancellationToken ct = default);

AcquireAsync throws LockAcquisitionException if it can't take the lock within wait. Every lock has an expiry (TTL) so a crashed holder can't wedge the resource forever.

The Redis provider targets a single Redis endpoint (SET NX PX + owner-checked release), which is the right trade-off for the vast majority of apps. It is not a multi-node Redlock quorum.

Repository layout

GM.DistributedLock/              # IDistributedLock, ILockHandle, in-memory implementation
GM.DistributedLock.Redis/        # Redis implementation (StackExchange.Redis)
tests/GM.DistributedLock.Tests/  # xUnit tests for the in-memory lock

Building & testing

dotnet build -c Release
dotnet test  -c Release

Releasing

Versioning is automated from Conventional Commits — see CONTRIBUTING.md. Both packages share one version (Directory.Build.props) and publish together to nuget.org on each release.

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 94 8/2/2026