Install-Package ModernCaching -Version 0.0.2
dotnet add package ModernCaching --version 0.0.2
<PackageReference Include="ModernCaching" Version="0.0.2" />
paket add ModernCaching --version 0.0.2
#r "nuget: ModernCaching, 0.0.2"
// Install ModernCaching as a Cake Addin #addin nuget:?package=ModernCaching&version=0.0.2 // Install ModernCaching as a Cake Tool #tool nuget:?package=ModernCaching&version=0.0.2
A 2-layer, performant and resilient caching solution for modern .NET.
A typical cache provided by this library consists of:
- a synchronous local cache that implements
- an asynchronous distributed cache that implements
IAsyncCache(e.g. memcache, redis)
- a data source that implements
IDataSource(e.g. relational database, Web API, CPU intensive task...)
ModernCaching is available on Nuget.
dotnet add package ModernCaching
- Strict API.
IReadOnlyCachehas only two methods:
TryPeek, a synchronous operation to only get the value if it's present in the local cache and refresh in the background if needed.
TryGetAsync, an asynchronous operation to get the first fresh value in the local cache, distributed cache or the data source, in that order.
- Performance. Unlike other caching libraries that use a
stringas a key or an
objectas value or both,
ModernCachinguses a generic key and value. That way, getting a value from the local cache doesn't require any allocation for simple type keys such as
intor more complex user-defined objects. See the benchmarks.
- Resilience. With its fixed number of layers, each behavior is clearly defined when one of these layers is down. For instance, the data source is skipped if the distributed cache is unavailable to avoid DDOSing it.
- Instrumentation. Metrics are exposed using OpenTelemetry API. Errors from user-code are logged if a logger is specified.
This example caches the user information. The first layer is implemented with an
in-memory cache, the second one is a redis where we specify how to create the
key and how to serialize the value using the interface
Behind these two layers stands the
var cache = await new ReadOnlyCacheBuilder<Guid, User>("user-cache", new UserDataSource("Host=localhost;User ID=postgres")) .WithLocalCache(new MemoryCache<Guid, User>()) .WithDistributedCache(new RedisAsyncCache(redis), new ProtobufKeyValueSerializer<Guid, User>()) .BuildAsync(); Guid userId = new("cb22ff11-4683-4ec3-b212-7f1d0ab378cc"); bool found = cache.TryPeek(userId, out User? user); // Only check local cache with background refresh. (bool found, User? user) = await cache.TryGetAsync(userId); // Check all layers for a fresh value.
The rest of the code as well as other examples can be found in src/ModernCaching.ITest.
Benchmark of the very hot path of different caching libraries (CacheTower, Foundatio, LazyCache, FusionCache, EasyCaching, CacheManager), that is, getting locally cached data. The .NET ConcurrentDictionary was also added as a baseline.
|ConcurrentDictionary||9.728 ns||0.1687 ns||0.1578 ns||1.00||0.00||-|
|ModernCaching||23.887 ns||0.1283 ns||0.1200 ns||2.46||0.04||-|
|CacheTower||111.146 ns||0.6491 ns||0.5754 ns||11.44||0.21||96 B|
|Foundatio||251.498 ns||0.2877 ns||0.2551 ns||25.88||0.43||216 B|
|LazyCache||258.821 ns||1.2548 ns||0.9797 ns||26.64||0.43||96 B|
|FusionCache||292.959 ns||1.8200 ns||1.6134 ns||30.15||0.52||184 B|
|EasyCaching||383.052 ns||0.2729 ns||0.2419 ns||39.42||0.65||264 B|
|CacheManager||465.721 ns||0.5287 ns||0.4687 ns||47.93||0.76||344 B|
This library has similar performance as a raw ConcurrentDictionary since its hot path is a thin layer around it. It doesn't allocate anything, putting no pressure on the garbage collector.
Code can be found in src/ModernCaching.Benchmarks.
All code found in this repository is licensed under MIT. See the LICENSE file in the project root for the full license text.
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.