Soenneker.Deduplication.Redis
4.0.87
Prefix Reserved
dotnet add package Soenneker.Deduplication.Redis --version 4.0.87
NuGet\Install-Package Soenneker.Deduplication.Redis -Version 4.0.87
<PackageReference Include="Soenneker.Deduplication.Redis" Version="4.0.87" />
<PackageVersion Include="Soenneker.Deduplication.Redis" Version="4.0.87" />
<PackageReference Include="Soenneker.Deduplication.Redis" />
paket add Soenneker.Deduplication.Redis --version 4.0.87
#r "nuget: Soenneker.Deduplication.Redis, 4.0.87"
#:package Soenneker.Deduplication.Redis@4.0.87
#addin nuget:?package=Soenneker.Deduplication.Redis&version=4.0.87
#tool nuget:?package=Soenneker.Deduplication.Redis&version=4.0.87
Soenneker.Deduplication.Redis
Distributed, time-bounded duplicate suppression using atomic Redis markers.
Installation
dotnet add package Soenneker.Deduplication.Redis
Registration
using Soenneker.Deduplication.Redis.Registrars;
services.AddRedisDedupeAsSingleton();
This also registers Soenneker.Redis.Client; configure its Redis connection using that package’s normal configuration.
Usage
using Soenneker.Deduplication.Redis.Abstract;
public sealed class WebhookConsumer(IRedisDedupe dedupe)
{
public async ValueTask Handle(string eventId, CancellationToken cancellationToken)
{
bool firstDelivery = await dedupe.TryMarkSeen(
cacheKey: "dedupe:webhooks:stripe",
cacheValue: eventId,
expiration: TimeSpan.FromHours(24),
cancellationToken);
if (!firstDelivery)
return;
await ProcessWebhook(cancellationToken);
}
}
TryMarkSeen uses Redis SET ... NX with the TTL in the same command. Concurrent callers across processes get one true result while the marker exists; later callers get false. Redis connection and command failures propagate instead of being reported as duplicates.
Lookup and removal
bool seen = await dedupe.Contains("dedupe:webhooks:stripe", eventId, cancellationToken);
bool removed = await dedupe.TryRemove("dedupe:webhooks:stripe", eventId, cancellationToken);
Contains does not refresh the TTL. TryRemove performs one atomic Redis delete and returns whether a marker was deleted.
String and ReadOnlySpan<char> overloads hash UTF-16 characters. TryMarkSeenUtf8, ContainsUtf8, and TryRemoveUtf8 hash the supplied bytes. Use the same representation for every operation on a value; the UTF-16 string "abc" and its UTF-8 bytes are different dedupe keys.
Redis keys and retention
The stored key is:
{cacheKey}:{lowercase 16-character XXH3-64 hash}
Only the 64-bit hash marker is stored as the value, but cacheKey remains visible in Redis. Choose a stable, non-secret namespace. Different inputs can theoretically collide, so use a full-key design when collision tolerance is unacceptable.
Always set an expiration for open-ended inputs unless permanent markers and their storage growth are intentional. A non-positive expiration is rejected; null creates a marker without automatic expiry.
Delivery semantics
This package is a duplicate-suppression primitive, not an exactly-once transaction. The marker and your business operation are separate: a process can fail after claiming a value but before completing the work. For durable message processing, combine it with an inbox/outbox or another transactional idempotency design. Redis eviction, data loss, manual deletion, or TTL expiry can also make a value eligible again.
| Product | Versions 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. |
-
net10.0
- Soenneker.Hashing.XxHash (>= 4.0.87)
- Soenneker.Redis.Client (>= 4.0.2314)
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 |
|---|---|---|
| 4.0.87 | 30 | 9/6/2026 |
| 4.0.86 | 38 | 9/5/2026 |
| 4.0.85 | 47 | 9/5/2026 |
| 4.0.84 | 38 | 9/5/2026 |
| 4.0.83 | 35 | 9/4/2026 |
| 4.0.81 | 44 | 9/4/2026 |
| 4.0.80 | 39 | 9/4/2026 |
| 4.0.79 | 43 | 9/4/2026 |
| 4.0.78 | 38 | 9/4/2026 |
| 4.0.77 | 71 | 9/1/2026 |
| 4.0.76 | 77 | 9/1/2026 |
| 4.0.74 | 82 | 8/31/2026 |
| 4.0.73 | 83 | 8/31/2026 |
| 4.0.72 | 81 | 8/31/2026 |
| 4.0.71 | 76 | 8/31/2026 |
| 4.0.70 | 82 | 8/30/2026 |
| 4.0.69 | 79 | 8/30/2026 |
| 4.0.68 | 83 | 8/30/2026 |
| 4.0.67 | 91 | 8/30/2026 |
| 4.0.66 | 82 | 8/29/2026 |