Celerity.Ring
2.4.0
See the version list below for details.
dotnet add package Celerity.Ring --version 2.4.0
NuGet\Install-Package Celerity.Ring -Version 2.4.0
<PackageReference Include="Celerity.Ring" Version="2.4.0" />
<PackageVersion Include="Celerity.Ring" Version="2.4.0" />
<PackageReference Include="Celerity.Ring" />
paket add Celerity.Ring --version 2.4.0
#r "nuget: Celerity.Ring, 2.4.0"
#:package Celerity.Ring@2.4.0
#addin nuget:?package=Celerity.Ring&version=2.4.0
#tool nuget:?package=Celerity.Ring&version=2.4.0
Celerity.Ring
Deterministic consistent-hashing and rendezvous (HRW) rings for sharding and request routing — generic over your key type and a zero-cost, JIT-inlined Celerity hasher.
dotnet add package Celerity.Ring
The BCL has no consistent-hashing type, so the status quo is "hand-roll a SortedDictionary<uint, TNode> + a hash, or take a stale NuGet." Celerity.Ring fills that gap and adds the property a sharded fleet actually needs: byte-identical key→node assignment across processes, runtimes, and CPU architectures (x64 / arm64 / Blazor WASM). A ring built from string.GetHashCode() reshards silently between processes because that hash is randomized per run; Celerity.Ring routes through a specified deterministic hash (StringXxHash3Hasher) and pure integer mixing — no randomized hash, no float/log, no endianness-dependent step.
Consistent-hash ring
using Celerity.Ring;
var ring = new StringConsistentHashRing<string>(); // string keys, deterministic StringXxHash3Hasher
ring.Add("cache-a", "10.0.0.1");
ring.Add("cache-b", "10.0.0.2");
ring.Add("cache-c", "10.0.0.3", weight: 2); // twice the virtual nodes -> ~2x the keys
string owner = ring.GetNode("user:42"); // single node for this key
var replicas = ring.GetReplicas("user:42", 3); // primary + 2 distinct successors
ring.Remove("cache-b"); // only cache-b's ~1/N keys remap
Each node is placed at VirtualNodesPerNode (default 160) positions on a [0, 2^32) ring, so load is even and a departing node hands its keys to many successors instead of one. Adding/removing a node remaps only about 1/NodeCount of keys — versus the near-total reshuffle of hash % nodeCount.
Rendezvous (HRW) hash
No ring array, nothing to rebuild on membership change — ideal for small, churning clusters:
var pool = new StringRendezvousHash<string>();
pool.Add("node-1", "10.0.0.1");
pool.Add("node-2", "10.0.0.2");
string owner = pool.GetNode("tenant:7"); // highest-scoring node for this key
var ranked = pool.GetReplicas("tenant:7", 2); // ranked preference list
Scoring is pure-integer max (weights are realized as integer sub-labels), so HRW stays deterministic across platforms with no floating-point log. A lookup is O(NodeCount) scaled by total weight.
Generic over your key type
Both types are generic over TKey and a struct IHashProvider<TKey> the JIT inlines on the routing hot path:
using Celerity.Hashing;
var byId = new ConsistentHashRing<Endpoint, long, Int64WangNaiveHasher>();
var byGuid = new RendezvousHash<Endpoint, Guid, GuidHasher>();
For cross-process agreement, use a specified deterministic hasher (StringXxHash3Hasher, GuidHasher, an integer hasher) — not DefaultHasher<string>, which delegates to the per-run-randomized BCL string hash.
Concurrency
Reads (GetNode / TryGetNode / GetReplicas) are lock-free: each mutation publishes an immutable snapshot with a single volatile write, so a reader always sees a consistent topology. Mutations (Add / Remove) must be serialized by the caller.
Why managed (and not a native binding)
GetNode(key) is one chatty call over one small managed key on the request hot path. Binding a native ring (libketama) means marshaling the key on every lookup, it can only hash byte buffers (not your inlined generic hasher), and it needs a per-RID native binary that Blazor WASM / Native AOT / IL2CPP can't load. Determinism across a heterogeneous fleet is a correctness requirement a randomized GetHashCode and a per-arch .so cannot meet. A pure-managed ring runs identically on every RID with no native dependency.
Part of the Celerity family.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 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 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
- Celerity.Collections (>= 2.4.0)
-
net8.0
- Celerity.Collections (>= 2.4.0)
-
net9.0
- Celerity.Collections (>= 2.4.0)
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 |
|---|---|---|
| 2.4.1-beta.4 | 26 | 7/26/2026 |
| 2.4.0 | 38 | 7/26/2026 |
| 2.3.1-beta.15 | 45 | 7/25/2026 |
| 2.3.1-beta.8 | 39 | 7/23/2026 |
| 2.3.1-beta.2 | 36 | 7/22/2026 |
| 2.3.0 | 82 | 7/19/2026 |
| 2.2.1-beta.24 | 55 | 7/18/2026 |
| 2.2.1-beta.18 | 51 | 7/14/2026 |
| 2.2.1-beta.15 | 46 | 7/12/2026 |