diff --git a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs b/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs index 1bdfec2450..4b9d237ea4 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs @@ -1,6 +1,5 @@ using System.Text.RegularExpressions; using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; @@ -9,19 +8,17 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache; /// -/// Implements on top of a . +/// Implements on top of a . /// public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { - private readonly IOptions _options; - private readonly IHostEnvironment? _hostEnvironment; private readonly ISet _keys = new HashSet(); - private static readonly TimeSpan _readLockTimeout = TimeSpan.FromSeconds(5); - private static readonly TimeSpan _writeLockTimeout = TimeSpan.FromSeconds(5); - private readonly ReaderWriterLockSlim _locker = new(LockRecursionPolicy.SupportsRecursion); private bool _disposedValue; + private static readonly TimeSpan _readLockTimeout = TimeSpan.FromSeconds(5); + private static readonly TimeSpan _writeLockTimeout = TimeSpan.FromSeconds(5); + /// /// Gets the internal memory cache, for tests only! /// @@ -34,7 +31,7 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable /// Initializes a new instance of the . /// public ObjectCacheAppCache() - : this(Options.Create(new MemoryCacheOptions()), NullLoggerFactory.Instance, null) + : this(new MemoryCacheOptions(), NullLoggerFactory.Instance) { } /// @@ -42,14 +39,8 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable /// /// The options. /// The logger factory. - /// The host environment. - public ObjectCacheAppCache(IOptions options, ILoggerFactory loggerFactory, IHostEnvironment? hostEnvironment) - { - _options = options; - _hostEnvironment = hostEnvironment; - - MemoryCache = new MemoryCache(_options, loggerFactory); - } + internal ObjectCacheAppCache(IOptions options, ILoggerFactory loggerFactory) + => MemoryCache = new MemoryCache(options, loggerFactory); /// public object? Get(string key) @@ -59,8 +50,9 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { if (_locker.TryEnterReadLock(_readLockTimeout) is false) { - throw new TimeoutException("Timeout exceeded to the memory cache when getting item"); + throw new TimeoutException("Timeout exceeded to the memory cache when getting item by key."); } + result = MemoryCache.Get(key) as Lazy; // null if key not found } finally @@ -71,7 +63,9 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable } } - return result == null ? null : SafeLazy.GetSafeLazyValue(result); // return exceptions as null + return result is null + ? null + : SafeLazy.GetSafeLazyValue(result); // return exceptions as null } /// @@ -90,10 +84,11 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { if (_locker.TryEnterReadLock(_readLockTimeout) is false) { - throw new TimeoutException("Timeout exceeded to the memory cache when getting item"); + throw new TimeoutException("Timeout exceeded to the memory cache when searching items by predicate."); } + entries = _keys.Where(predicate) - .Select(key => MemoryCache.Get(key)) + .Select(MemoryCache.Get) .WhereNotNull() .ToArray(); // evaluate while locked } @@ -116,29 +111,29 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { // see notes in HttpRuntimeAppCache Lazy? result; - try { - _locker.EnterUpgradeableReadLock(); + if (_locker.TryEnterUpgradeableReadLock(_readLockTimeout) is false) + { + throw new TimeoutException("Timeout exceeded to the memory cache when getting item by key."); + } result = MemoryCache.Get(key) as Lazy; // get non-created as NonCreatedValue & exceptions as null - if (result == null || SafeLazy.GetSafeLazyValue(result, true) == null) + if (result is null || SafeLazy.GetSafeLazyValue(result, true) is null) { result = SafeLazy.GetSafeLazy(factory); - MemoryCacheEntryOptions options = GetOptions(timeout, isSliding); - try { if (_locker.TryEnterWriteLock(_writeLockTimeout) is false) { - throw new TimeoutException("Timeout exceeded to the memory cache when using factory to insert"); + throw new TimeoutException("Timeout exceeded to the memory cache when inserting item."); } // NOTE: This does an add or update - MemoryCache.Set(key, result, options); + MemoryCache.Set(key, result, GetOptions(timeout, isSliding)); _keys.Add(key); } finally @@ -158,7 +153,6 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable } } - // return result.Value; var value = result.Value; // will not throw (safe lazy) if (value is SafeLazy.ExceptionHolder eh) { @@ -175,21 +169,20 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable // and make sure we don't store a null value. Lazy result = SafeLazy.GetSafeLazy(factory); var value = result.Value; // force evaluation now - if (value == null) + if (value is null) { return; // do not store null values (backward compat) } - MemoryCacheEntryOptions options = GetOptions(timeout, isSliding); try { if (_locker.TryEnterWriteLock(_writeLockTimeout) is false) { - throw new TimeoutException("Cannot insert value into cache, due to timeout for lock."); + throw new TimeoutException("Timeout exceeded to the memory cache when inserting item."); } // NOTE: This does an add or update - MemoryCache.Set(key, result, options); + MemoryCache.Set(key, result, GetOptions(timeout, isSliding)); _keys.Add(key); } finally @@ -206,10 +199,12 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { try { - _locker.EnterWriteLock(); + if (_locker.TryEnterWriteLock(_writeLockTimeout) is false) + { + throw new TimeoutException("Timeout exceeded to the memory cache when clearing all items."); + } - MemoryCache.Dispose(); - MemoryCache = new MemoryCache(_options); + MemoryCache.Clear(); _keys.Clear(); } finally @@ -228,7 +223,7 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { if (_locker.TryEnterWriteLock(_writeLockTimeout) is false) { - throw new TimeoutException("Timeout exceeded to the memory cache when clearing item"); + throw new TimeoutException("Timeout exceeded to the memory cache when clearing item by key."); } MemoryCache.Remove(key); @@ -316,7 +311,7 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { if (_locker.TryEnterWriteLock(_writeLockTimeout) is false) { - throw new TimeoutException("Timeout exceeded to the memory cache."); + throw new TimeoutException("Timeout exceeded to the memory cache when clearing items by predicate."); } // ToArray required to remove @@ -335,9 +330,9 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable } } - public void Dispose() => + public void Dispose() // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(true); + => Dispose(true); protected virtual void Dispose(bool disposing) { @@ -353,7 +348,7 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable } } - private MemoryCacheEntryOptions GetOptions(TimeSpan? timeout = null, bool isSliding = false) + private MemoryCacheEntryOptions GetOptions(TimeSpan? timeout, bool isSliding) { var options = new MemoryCacheEntryOptions(); @@ -374,8 +369,9 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { if (_locker.TryEnterWriteLock(_writeLockTimeout) is false) { - throw new TimeoutException("Timeout exceeded to the memory cache when using factory to insert"); + throw new TimeoutException("Timeout exceeded to the memory cache when removing key."); } + _keys.Remove((string)key); } finally