KeyLockedMemoryCache 1.0.1

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

KeyLockedMemoryCache

KeyLockedMemoryCache

A high‑performance, per‑key locking cache built on top of .NET MemoryCache.
Designed for extreme concurrency, zero duplicated loads, and maximum speed.


🚀 Why KeyLockedMemoryCache?

MemoryCache is thread‑safe, but it does not prevent multiple threads from loading the same value at the same time.

This package solves that problem with a per‑key locking mechanism that guarantees:

  • Only one thread loads the value
  • All other threads wait and reuse the result
  • No duplicated work
  • No race conditions
  • No global locks
  • No performance bottlenecks

Perfect for:

  • High‑concurrency APIs
  • Microservices
  • Database‑heavy workloads
  • Expensive I/O operations
  • Distributed systems with local caching

✨ Key Features

  • 🔒 Per‑key locking
    Prevents duplicated loads under heavy concurrency.

  • Ultra‑fast access path
    Cache hits avoid locking and return immediately.

  • 🧹 Automatic lock cleanup
    Locks are removed when the cache entry expires.

  • 🧠 MemoryCache‑powered
    Leverages .NET's built‑in cache with expiration, eviction, and memory pressure handling.

  • 🧩 Zero dependencies
    Works everywhere: .NET Framework, .NET Standard, .NET Core, .NET 5/6/7/8.

  • 🧵 Thread‑safe by design
    Uses ConcurrentDictionary and fine‑grained locking.

  • 🛠️ Simple API
    Just call GetOrAdd(prefix, key, loader) and you're done.


📦 Installation

powershell:

-Install-Package KeyLockedMemoryCache

or

-dotnet add package KeyLockedMemoryCache

🧰 Usage Example (simple)

var cache = new InternalCache<string>(30); // 30 minutes expiration

string value = cache.GetOrAdd("User", userId, () ⇒ { // This code will run only once, even under heavy concurrency return LoadUserFromDatabase(userId); });

🏎️ High Concurrency Example

var cache = new InternalCache<Product>(10);

Parallel.For(0, 200, _ ⇒ { var result = cache.GetOrAdd("Product", 42, () ⇒ { // Expensive operation executed only once return LoadProductFromApi(42); }); });

##🧪 Performance Characteristics This cache is optimized for extreme read concurrency:

  • Cache hits: no locking, O(1) lookup
  • Cache misses: only one loader execution
  • Lock contention: limited to a single key
  • No global locks
  • Minimal allocations (optimized key building, static dictionaries, no closures)
  • Expiration handled by MemoryCache (no background threads) Benchmark Example
  • 200 threads
  • 500 iterations each
  • 100,000 total accesses
  • Loader executed: 1 time
  • Total time: 10–15 ms (depending on hardware)

##🧱 How it works (internals)

  • Build a fast key using string.Concat(prefix, ":", keyValue)
  • Try to get the value from MemoryCache (fast path)
  • If missing, obtain a lock object from a static ConcurrentDictionary
  • Enter a per‑key lock
  • Double‑check the cache (avoids race conditions)
  • Execute the loader only once
  • Store the value with a CacheItemPolicy
  • When the entry expires, the lock is automatically removed

##🔐 Thread Safety Guarantees

  • No two threads will execute the loader for the same key
  • No global lock is ever used
  • No deadlocks (locks are per‑key and short‑lived)
  • No memory leaks (locks are removed on expiration)
  • Safe for high‑concurrency scenarios (APIs, microservices, background jobs)

##🧹 Removing a value cache.Remove("User", userId);

This removes:

  • The cache entry
  • The associated lock

##🧩 API Summary | | | | GetOrAdd(prefix, key, loader) | | | Remove(prefix, key) | |

##📄 License MIT License.

##❤️ Contribute Issues and pull requests are welcome. If you find this useful, consider starring the repository

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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.1 157 12/26/2025
1.0.0 148 12/26/2025