ReactiveLock.Distributed.Redis
0.0.19
See the version list below for details.
dotnet add package ReactiveLock.Distributed.Redis --version 0.0.19
NuGet\Install-Package ReactiveLock.Distributed.Redis -Version 0.0.19
<PackageReference Include="ReactiveLock.Distributed.Redis" Version="0.0.19" />
<PackageVersion Include="ReactiveLock.Distributed.Redis" Version="0.0.19" />
<PackageReference Include="ReactiveLock.Distributed.Redis" />
paket add ReactiveLock.Distributed.Redis --version 0.0.19
#r "nuget: ReactiveLock.Distributed.Redis, 0.0.19"
#:package ReactiveLock.Distributed.Redis@0.0.19
#addin nuget:?package=ReactiveLock.Distributed.Redis&version=0.0.19
#tool nuget:?package=ReactiveLock.Distributed.Redis&version=0.0.19
ReactiveLock
ReactiveLock is a .NET 9 library for reactive, distributed lock coordination. It allows multiple application instances to track busy/idle state and react to state changes using async handlers.
It supports both in-process and distributed synchronization. Redis is the default distributed backend.
Packages
Package | Description |
---|---|
Core abstractions and in-process lock coordination | |
Adds DI and named resolution for distributed backends | |
Redis-based distributed lock synchronization |
Use only ReactiveLock.Core if you don't need distributed coordination.
Installation
In-process only:
dotnet add package ReactiveLock.Core
Distributed with Redis:
dotnet add package ReactiveLock.Core
dotnet add package ReactiveLock.DependencyInjection
dotnet add package ReactiveLock.Distributed.Redis
Usage
Simpler approach – Local-only (in-process)
Use this when you want a lightweight, in-memory, thread-coordinated lock mechanism within a single process.
using MichelOliveira.Com.ReactiveLock.Core;
// Create a new tracker state instance
var state = new ReactiveLockTrackerState();
// Set the local state as blocked (simulates a lock being held)
await state.SetLocalStateBlockedAsync();
// Start 3 tasks that will each wait for the state to become unblocked
var tasks = Enumerable.Range(1, 3).Select(i =>
Task.Run(async () => {
Console.WriteLine($"[Task {i}] Waiting...");
// Each task will wait here until the state becomes unblocked
await state.WaitIfBlockedAsync();
// Once unblocked, this message will print
Console.WriteLine($"[Task {i}] Proceeded.");
})
).ToArray();
// Simulate a delay before unblocking the state
await Task.Delay(1000);
// Unblock the state (releases all waiting tasks)
await state.SetLocalStateUnblockedAsync();
// Wait for all tasks to complete
await Task.WhenAll(tasks);
// Indicate completion
Console.WriteLine("Done.");
Controller-based (Increment / Decrement) local-only sample
Use this when you prefer reference-counted control using a controller abstraction (IncrementAsync / DecrementAsync), ideal for more complex coordination.
using MichelOliveira.Com.ReactiveLock.Core;
using System;
using System.Linq;
using System.Threading.Tasks;
var state = new ReactiveLockTrackerState();
var store = new InMemoryReactiveLockTrackerStore(state);
var controller = new ReactiveLockTrackerController(store);
// Initially block the state by incrementing (e.g. lock acquired)
await controller.IncrementAsync(); // Blocked
var tasks = Enumerable.Range(1, 3).Select(i =>
Task.Run(async () =>
{
Console.WriteLine($"[Task {i}] Waiting...");
await state.WaitIfBlockedAsync(); // Wait while blocked
Console.WriteLine($"[Task {i}] Proceeded.");
})
).ToArray();
// Simulate some delay before unblocking
await Task.Delay(1000);
// Decrement to unblock (lock released)
await controller.DecrementAsync(); // Unblocked
await Task.WhenAll(tasks);
Console.WriteLine("Done.");
Expected Output (both examples)
[Task 3] Waiting...
[Task 1] Waiting...
[Task 2] Waiting...
[Task 3] Proceeded.
[Task 2] Proceeded.
[Task 1] Proceeded.
Distributed HTTP Client Request Counter (Redis)
Setup
builder.Services.InitializeDistributedRedisReactiveLock(Dns.GetHostName());
builder.Services.AddDistributedRedisReactiveLock("http");
builder.Services.AddTransient<CountingHandler>();
builder.Services.AddHttpClient("http", client =>
client.BaseAddress = new Uri(builder.Configuration.GetConnectionString("http")!))
.AddHttpMessageHandler<CountingHandler>();
var app = builder.Build();
await app.UseDistributedRedisReactiveLockAsync();
CountingHandler
public class CountingHandler : DelegatingHandler
{
private readonly IReactiveLockTrackerController _controller;
public CountingHandler(IReactiveLockTrackerFactory factory)
{
_controller = factory.GetTrackerController("http");
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
await _controller.IncrementAsync();
try
{
return await base.SendAsync(request, cancellationToken);
}
finally
{
await _controller.DecrementAsync();
}
}
}
Expected Behavior
- Each HTTP request increments the "http" lock counter.
- On response, the counter is decremented.
- Lock state is shared across all application instances.
- You can use the lock state to:
- Check if any requests are active.
- Wait for all requests to complete.
Use Case Example
var state = factory.GetTrackerState("http");
if (await state.IsBlockedAsync())
{
Console.WriteLine("HTTP requests active.");
}
await state.WaitIfBlockedAsync();
Console.WriteLine("No active HTTP requests.");
Requirements
- .NET 9 SDK
License
MIT © Michel Oliveira
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. 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 was computed. 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. |
-
net9.0
- Microsoft.AspNetCore.Http.Abstractions (>= 2.3.0)
- StackExchange.Redis (>= 2.8.58)
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 |
---|---|---|
0.0.21 | 502 | 7/30/2025 |
0.0.20 | 92 | 7/30/2025 |
0.0.19 | 113 | 7/29/2025 |
0.0.17 | 91 | 7/29/2025 |
0.0.16 | 84 | 7/29/2025 |
0.0.15 | 172 | 7/26/2025 |
0.0.13 | 139 | 7/26/2025 |
0.0.12 | 203 | 7/26/2025 |
0.0.11 | 275 | 7/25/2025 |
0.0.9 | 279 | 7/25/2025 |
0.0.8 | 294 | 7/25/2025 |
0.0.7 | 297 | 7/25/2025 |
0.0.6 | 304 | 7/25/2025 |
0.0.5 | 321 | 7/25/2025 |
0.0.4 | 321 | 7/25/2025 |
0.0.3 | 318 | 7/25/2025 |
0.0.2 | 327 | 7/25/2025 |
0.0.1 | 325 | 7/25/2025 |