< Summary

Information
Class: AmbientServices.AmbientTwoStageCache<TOWNER>
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/Helpers/AmbientTwoStageCache.cs
Tag: 332_35464845198
Line coverage
100%
Covered lines: 4
Uncovered lines: 0
Coverable lines: 4
Total lines: 164
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%11100%

File(s)

/home/runner/work/AmbientServices/AmbientServices/AmbientServices/Helpers/AmbientTwoStageCache.cs

#LineLine coverage
 1using System;
 2using System.Threading;
 3using System.Threading.Tasks;
 4
 5namespace AmbientServices;
 6
 7/// <summary>
 8/// A class that provides caching using the local cache, falling back to the shared cache if not found, and storing/dele
 9/// </summary>
 10/// <remarks>
 11/// <pitch>Two-tier caching in one call: reads prefer the fast in-process tier while stores and removals are applied to 
 12/// <pledge>
 13/// Stores and removals are applied to the local tier and then the shared tier; the two writes are not transactional, so
 14/// Retrieval prefers the local tier and falls back to the shared tier on a local miss (or when no local cache is in eff
 15/// Disposable items (<see cref="IDisposable"/> or <see cref="IAsyncDisposable"/>) must never be cached here, for the re
 16/// All keys are prefixed with the owner type's name (or the supplied prefix) before reaching either tier.
 17/// When neither tier's service exists, every operation quietly succeeds without caching.
 18/// Clearing clears both underlying caches in their entirety, not merely this owner's entries.
 19/// </pledge>
 20/// <plan>
 21/// A stateless composition over two <see cref="AmbientService{T}"/> accessors (local and shared) with optional explicit
 22/// Local stores pass dispose-on-discard as false because anything cached here must also survive serialization to the sh
 23/// Cost and durability per tier are exactly those of the underlying caches.
 24/// </plan>
 25/// <priority>
 26/// 1. Fast local reads over agreement between the tiers: retrieval prefers the local tier and reaches the shared one on
 27/// 2. Simplicity over consistency between the tiers: stores and removals are applied local-then-shared with no coordina
 28/// </priority>
 29/// </remarks>
 30public class AmbientTwoStageCache
 31{
 32    private static readonly AmbientService<IAmbientLocalCache> _LocalCache = Ambient.GetService<IAmbientLocalCache>();
 33    private static readonly AmbientService<IAmbientSharedCache> _SharedCache = Ambient.GetService<IAmbientSharedCache>()
 34
 35    private readonly Type _type;
 36    private readonly string _defaultCachePrefix;
 37    private readonly IAmbientLocalCache? _explicitLocalCache;
 38    private readonly IAmbientSharedCache? _explicitSharedCache;
 39    private readonly string _cacheKeyPrefix;
 40
 41    /// <summary>
 42    /// Creates the AmbientTwoStageCache using the ambient cache service.
 43    /// </summary>
 44    /// <param name="ownerType">The <see cref="Type"/> for the owner.</param>
 45    /// <param name="cacheKeyPrefix">An optional cache key prefix for all items cached through this class.  Uses the typ
 46    public AmbientTwoStageCache(Type ownerType, string? cacheKeyPrefix = null)
 47        : this(ownerType, null, null, cacheKeyPrefix)
 48    {
 49    }
 50    /// <summary>
 51    /// Creates the AmbientTwoStageCache using the specified cache service.
 52    /// </summary>
 53    /// <param name="ownerType">The <see cref="Type"/> for the owner.</param>
 54    /// <param name="localCache">An explicit <see cref="IAmbientLocalCache"/> to use.</param>
 55    /// <param name="sharedCache">An explicit <see cref="IAmbientSharedCache"/> to use.</param>
 56    /// <param name="cacheKeyPrefix">An optional cache key prefix for all items cached through this class.  Uses the typ
 57    public AmbientTwoStageCache(Type ownerType, IAmbientLocalCache? localCache, IAmbientSharedCache? sharedCache, string
 58    {
 59        _type = ownerType;
 60        _defaultCachePrefix = $"{_type.Name}-";
 61        _explicitLocalCache = localCache;
 62        _explicitSharedCache = sharedCache;
 63        _cacheKeyPrefix = cacheKeyPrefix ?? _defaultCachePrefix;
 64    }
 65    /// <summary>
 66    /// Retrieves the item with the specified key from the cache (if possible).
 67    /// </summary>
 68    /// <typeparam name="T">The type of the cached object.</typeparam>
 69    /// <param name="itemKey">The unique key used when the object was cached.</param>
 70    /// <param name="refresh">An optional <see cref="TimeSpan"/> indicating the length of time to extend the lifespan of
 71    /// <param name="cancel">The optional <see cref="CancellationToken"/>.</param>
 72    /// <returns>The cached object, or null if it was not found in the cache.</returns>
 73    public async ValueTask<T?> Retrieve<T>(string itemKey, TimeSpan? refresh = null, CancellationToken cancel = default)
 74    {
 75        string key = _cacheKeyPrefix + itemKey;
 76        IAmbientLocalCache? localCache = _explicitLocalCache ?? _LocalCache.Local;
 77        IAmbientSharedCache? sharedCache = _explicitSharedCache ?? _SharedCache.Local;
 78        // prefer the local tier
 79        if (localCache != null)
 80        {
 81            T? local = await localCache.Retrieve<T>(key, refresh, cancel);
 82            if (local != null) return local;
 83        }
 84        // fall back to the shared tier on a local miss (or when no local cache is in effect)
 85        if (sharedCache != null) return await sharedCache.Retrieve<T>(key, refresh, cancel);
 86        return null;
 87    }
 88    /// <summary>
 89    /// Stores the specified item in the cache.
 90    /// </summary>
 91    /// <typeparam name="T">The type of the item to be cached.</typeparam>
 92    /// <param name="itemKey">A string that uniquely identifies the item being cached.</param>
 93    /// <param name="item">The item to be cached.</param>
 94    /// <param name="maxCacheDuration">An optional <see cref="TimeSpan"/> indicating the maximum amount of time to keep 
 95    /// <param name="expiration">An optional <see cref="DateTime"/> indicating a fixed time for when the item should exp
 96    /// <param name="cancel">The optional <see cref="CancellationToken"/>.</param>
 97    /// <remarks>
 98    /// If both <paramref name="expiration"/> and <paramref name="maxCacheDuration"/> are set, the earlier expiration wi
 99    /// <paramref name="item"/> must be serializable and must not be disposable: the local tier is written without dispo
 100    /// </remarks>
 101    public async ValueTask Store<T>(string itemKey, T item, TimeSpan? maxCacheDuration = null, DateTime? expiration = nu
 102    {
 103        string key = _cacheKeyPrefix + itemKey;
 104        IAmbientLocalCache? localCache = _explicitLocalCache ?? _LocalCache.Local;
 105        if (localCache != null) await localCache.Store(key, item, false, maxCacheDuration, expiration, cancel);
 106        IAmbientSharedCache? sharedCache = _explicitSharedCache ?? _SharedCache.Local;
 107        if (sharedCache != null) await sharedCache.Store(key, item, maxCacheDuration, expiration, cancel);
 108    }
 109    /// <summary>
 110    /// Removes the specified item from the cache.
 111    /// </summary>
 112    /// <typeparam name="T">The type of the item to be cached.</typeparam>
 113    /// <param name="itemKey">A string that uniquely identifies the item being cached.</param>
 114    /// <param name="cancel">The optional <see cref="CancellationToken"/>.</param>
 115    public async ValueTask Remove<T>(string itemKey, CancellationToken cancel = default) where T : class
 116    {
 117        string key = _cacheKeyPrefix + itemKey;
 118        IAmbientLocalCache? localCache = _explicitLocalCache ?? _LocalCache.Local;
 119        if (localCache != null) await localCache.Remove<T>(key, cancel);
 120        IAmbientSharedCache? sharedCache = _explicitSharedCache ?? _SharedCache.Local;
 121        if (sharedCache != null) await sharedCache.Remove<T>(key, cancel);
 122    }
 123    /// <summary>
 124    /// Flushes everything from the cache.
 125    /// </summary>
 126    /// <param name="cancel">The optional <see cref="CancellationToken"/>.</param>
 127    public async ValueTask Clear(CancellationToken cancel = default)
 128    {
 129        IAmbientLocalCache? localCache = _explicitLocalCache ?? _LocalCache.Local;
 130        if (localCache != null) await localCache.Clear(cancel);
 131        IAmbientSharedCache? sharedCache = _explicitSharedCache ?? _SharedCache.Local;
 132        if (sharedCache != null) await sharedCache.Clear(cancel);
 133    }
 134}
 135
 136/// <summary>
 137/// A generic type-specific two-stage cache owner class.  The name of the type is prepended to each cache key.
 138/// </summary>
 139/// <typeparam name="TOWNER">The type that owns the log messages.</typeparam>
 140/// <remarks>
 141/// <pitch>The usual way to declare a two-stage cache: the owner is a type parameter, so the key prefix is derived at co
 142/// <pledge><see cref="AmbientTwoStageCache"/></pledge>
 143/// <plan>Passes <c>typeof(TOWNER)</c> to the base class; adds no behavior of its own.</plan>
 144/// </remarks>
 145public class AmbientTwoStageCache<TOWNER> : AmbientTwoStageCache
 146{
 147    /// <summary>
 148    /// Creates the AmbientTwoStageCache using the ambient cache service.
 149    /// </summary>
 150    /// <param name="cacheKeyPrefix">An optional cache key prefix for all items cached through this class.  Uses the typ
 151    public AmbientTwoStageCache(string? cacheKeyPrefix = null)
 2152        : this(null, null, cacheKeyPrefix)
 153    {
 2154    }
 155    /// <summary>
 156    /// Creates the AmbientTwoStageCache using the specified cache service.
 157    /// </summary>
 158    /// <param name="localCache">An explicit <see cref="IAmbientLocalCache"/> to use.</param>
 159    /// <param name="sharedCache">An explicit <see cref="IAmbientSharedCache"/> to use.</param>
 160    /// <param name="cacheKeyPrefix">An optional cache key prefix for all items cached through this class.  Uses the typ
 2161    public AmbientTwoStageCache(IAmbientLocalCache? localCache, IAmbientSharedCache? sharedCache, string? cacheKeyPrefix
 162    {
 2163    }
 164}