Soenneker.Deduplication.Redis
4.0.71
Prefix Reserved
dotnet add package Soenneker.Deduplication.Redis --version 4.0.71
NuGet\Install-Package Soenneker.Deduplication.Redis -Version 4.0.71
<PackageReference Include="Soenneker.Deduplication.Redis" Version="4.0.71" />
<PackageVersion Include="Soenneker.Deduplication.Redis" Version="4.0.71" />
<PackageReference Include="Soenneker.Deduplication.Redis" />
paket add Soenneker.Deduplication.Redis --version 4.0.71
#r "nuget: Soenneker.Deduplication.Redis, 4.0.71"
#:package Soenneker.Deduplication.Redis@4.0.71
#addin nuget:?package=Soenneker.Deduplication.Redis&version=4.0.71
#tool nuget:?package=Soenneker.Deduplication.Redis&version=4.0.71
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.82)
- Soenneker.Redis.Client (>= 4.0.2299)
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.71 | 0 | 8/31/2026 |
| 4.0.70 | 30 | 8/30/2026 |
| 4.0.69 | 28 | 8/30/2026 |
| 4.0.68 | 36 | 8/30/2026 |
| 4.0.67 | 46 | 8/30/2026 |
| 4.0.66 | 36 | 8/29/2026 |
| 4.0.65 | 41 | 8/27/2026 |
| 4.0.64 | 45 | 8/26/2026 |
| 4.0.63 | 42 | 8/26/2026 |
| 4.0.62 | 42 | 8/26/2026 |
| 4.0.61 | 41 | 8/26/2026 |
| 4.0.60 | 92 | 8/22/2026 |
| 4.0.59 | 92 | 8/22/2026 |
| 4.0.58 | 87 | 8/21/2026 |
| 4.0.57 | 93 | 8/21/2026 |
| 4.0.56 | 95 | 8/19/2026 |
| 4.0.55 | 83 | 8/19/2026 |
| 4.0.54 | 94 | 8/18/2026 |
| 4.0.53 | 88 | 8/18/2026 |
| 4.0.52 | 83 | 8/18/2026 |