LazyCache Documentation

repository·master·Indexed 23 days ago

https://github.com/alastairtree/lazycache

A thread-safe, in-memory caching service for .NET that wraps Microsoft.Extensions.Caching and Lazy<T>. It prevents cache stampedes by guaranteeing that factory delegates are executed only once, even under heavy load. Features include strongly typed generics, async support via GetOrAddAsync(), exception safety, and configurable expiration modes (LazyExpiration and ImmediateEviction). Supports .NET Standard 2.0+, .NET Core 2+, and .NET Framework 4.0+.

Tokens
1.7K
Snippets
3
Records
12
Agent score
82%

What's inside LazyCache

  1. What is LazyCache and when should I use it?

    master

    LazyCache is a simple, thread-safe, in-memory caching service. It is designed to wrap Microsoft.Extensions.Caching and Lazy<T> to provide a developer-friendly, generics-based API.

    Use cases:

    • Caching database calls.
    • Caching complex object graph building routines.
    • Caching web service calls.

    Key characteristics:

    • Guaranteed single evaluation: The factory delegate is guaranteed to run only once.
    • Strongly typed: Uses generics so you don't need to cast objects when retrieving them.
    • Exception safety: It prevents inadvertently caching an exception by removing Lazy objects that evaluate to an exception.
    • Async support: Provides GetOrAddAsync() for single evaluation of async delegates.
  2. Understand LazyCache benchmark types

    master

    The LazyCache.Benchmarks project provides two categories of benchmarks:

    Basics

    Small, focused benchmarks that test individual aspects of LazyCache. These use the standard .NET MemoryCache as a baseline to measure the performance overhead (the "cost") of using LazyCache.

    Integration

    Benchmarks that simulate full use-cases by chaining multiple operations. These are designed to verify complex behaviors, such as ensuring that concurrent calls to initialize a cache item correctly result in only one instance being created while subsequent calls await that same result.

  3. Implement a custom cache provider using ICacheProvider

    master
    Since version 2.0.0, LazyCache uses a provider model. While it defaults to a singleton shared in-memory cache, you can implement your own storage logic by implementing the ICacheProvider interface. You can then access the provider via IAppCache.CacheProvider or provide your own via the IAppCache constructor.
  4. Configure expiration modes in LazyCache

    master

    Starting from version 2.1.0, you can specify how items are removed from the cache using ExpirationMode.

    • ExpirationMode.ImmediateEviction (formerly ExpirationMode.ImmediateExpiration in 2.1.0): Uses a timer to remove items from the cache as soon as they expire. This is more resource-intensive.
    • ExpirationMode.LazyExpiration: The default mode. Expired items are removed only when they are next accessed.

    Note: In version 2.1.3, ExpirationMode.ImmediateExpiration was renamed to ExpirationMode.ImmediateEviction.

  5. Run LazyCache benchmarks

    master

    To run the performance benchmarks for LazyCache using BenchmarkDotNet, follow these steps:

    1. Ensure you have the required .NET SDKs installed (check LazyCache.Benchmarks.csproj for specific versions).
    2. Clone the repository.
    3. Open a terminal and navigate to the LazyCache.Benchmarks directory.
    4. Execute the following command:
      dotnet run -c Release
    5. Select your desired benchmark suite from the numeric menu provided in the terminal.

    To run a specific subset of benchmarks (for example, after modifying a specific method), use a filter flag:

    • To run only IAppCache.Get implementations: dotnet run -c Release -- -f *Get
    • To run *GetOrAddAsync implementations: dotnet run -c Release -- -f *GetOrAddAsync
    dotnet run -c Release
  6. Migrate from version 1.x to 2.0.0

    master

    Version 2.0.0 introduced several breaking changes that require migration:

    1. Framework: Upgraded to netstandard2.0.
    2. Underlying Cache: Changed from System.Runtime.Caching to Microsoft.Extensions.Caching.Memory.
    3. Provider Model: IAppCache.ObjectCache was removed. Use IAppCache.CacheProvider to access the underlying cache.
    4. Policy Objects: CacheItemPolicy was replaced by MemoryCacheEntryOptions.
    5. Callbacks: RemovedCallback was renamed to PostEvictionCallbacks.
    6. API Structure: Most CachingService method overloads were moved to extension methods on IAppCache (found in AppCacheExtensions).
    7. Default Policy: Use IAppCache.DefaultCachePolicy instead of CachingService.DefaultCacheDuration.
  7. Avoid allocation monitoring pitfalls in benchmarks

    master

    When running or creating custom benchmarks, be aware that BenchmarkDotNet only monitors allocations within the specific benchmark method being executed.

    Important: Because the default instance of MemoryCacheProvider is static, allocations made into that cache will not be captured by the MemoryDiagnoser. To ensure accurate allocation tracking, always create new instances of the Service, Provider, and the backing Cache for each benchmark run.

  8. Use GetOrAdd to cache values with a factory delegate

    master

    The core pattern in LazyCache is using GetOrAdd(). Instead of manually checking the cache and then adding a value, you provide a factory delegate (Func<T>). LazyCache ensures that the delegate is executed only once (atomically) even under heavy load, and then caches the result. This prevents the 'cache stampede' problem where multiple threads try to re-calculate the same expensive value simultaneously.

    // Create our cache service using the defaults (Dependency injection ready).
    IAppCache cache = new CachingService();
    
    // Declare (but don't execute) a func/delegate whose result we want to cache
    Func<ComplexObjects> complexObjectFactory = () => methodThatTakesTimeOrResources();
    
    // Get our ComplexObjects from the cache, or build them in the factory func 
    // and cache the results for next time under the given key
    ComplexObjects cachedResults = cache.GetOrAdd("uniqueKey", complexObjectFactory);
  9. Use ICacheEntry factory methods for fine-grained control

    master

    In version 2.0.0, new overloads were added to IAppCache that allow you to specify cache expiry options at the time the item factory is executed. This allows you to use ICacheEntry to set specific policies for that specific entry.

    Available methods:

    • GetOrAdd<T>(string key, Func<ICacheEntry, T> addItemFactory)
    • Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task<T>> addItemFactory)
  10. Compatibility and .NET support

    master

    LazyCache version support based on your target framework:

    Target FrameworkRecommended LazyCache Version
    .NET Standard 2.0+2.0 or above
    .NET Core 2+2.0 or above
    .NET Framework 4.6.1+2.0 or above
    .NET Framework (no Std 2 support, e.g., 4.5, 4.5.1, 4.6)0.7 - 1.x
    .NET Framework 4.00.6

    Note: For new projects, consider using Microsoft's HybridCache as it solves similar problems and is part of the framework.