From d8b5326809882f6d730b79cd8980399e8ce1b16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Flemstr=C3=B6m?= Date: Thu, 3 Feb 2022 16:21:53 +0100 Subject: [PATCH] Introducing two level distributed cache --- .../Piranha/Cache/DistributedTwoLevelCache.cs | 211 +++++++++++++ .../DistributedTwoLevelCacheWithClone.cs | 31 ++ .../PiranhaDistributedCacheExtensions.cs | 42 +++ .../Piranha.Tests/DistributedTwoLevelCache.cs | 293 ++++++++++++++++++ test/Piranha.Tests/Services/AliasTests.cs | 15 +- test/Piranha.Tests/Services/CommentTests.cs | 11 + test/Piranha.Tests/Services/ContentTests.cs | 10 + test/Piranha.Tests/Services/MediaTests.cs | 11 + test/Piranha.Tests/Services/PageTests.cs | 11 + test/Piranha.Tests/Services/PageTypeTests.cs | 11 + test/Piranha.Tests/Services/ParamTests.cs | 11 + test/Piranha.Tests/Services/PostTests.cs | 11 + test/Piranha.Tests/Services/PostTypeTests.cs | 11 + test/Piranha.Tests/Services/SiteTests.cs | 11 + test/Piranha.Tests/Services/SiteTypeTests.cs | 11 + 15 files changed, 700 insertions(+), 1 deletion(-) create mode 100644 core/Piranha/Cache/DistributedTwoLevelCache.cs create mode 100644 core/Piranha/Cache/DistributedTwoLevelCacheWithClone.cs create mode 100644 test/Piranha.Tests/DistributedTwoLevelCache.cs diff --git a/core/Piranha/Cache/DistributedTwoLevelCache.cs b/core/Piranha/Cache/DistributedTwoLevelCache.cs new file mode 100644 index 000000000..5f151e6eb --- /dev/null +++ b/core/Piranha/Cache/DistributedTwoLevelCache.cs @@ -0,0 +1,211 @@ +/* + * Copyright (c) .NET Foundation and Contributors + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Text; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.Memory; +using Newtonsoft.Json; + +namespace Piranha.Cache +{ + /// + /// Two level distributed cache. The first level is a per application memory cache. + /// The second level is the distributed cache + /// + public class DistributedTwoLevelCache : ICache + { + private readonly IMemoryCache _firstLevelCache; + private readonly IDistributedCache _secondLevelCache; + + private readonly JsonSerializerSettings _jsonSettings; + private readonly bool _clone; + private string _cacheVersion; + + public const string CACHE_VERSION = "piranha_cache_version"; + + /// + /// Default constructor. + /// + /// First level memory cache + /// Second level distributed cache + public DistributedTwoLevelCache(IMemoryCache firstLevelCache, IDistributedCache secondLevelCache) + { + _firstLevelCache = firstLevelCache; + _secondLevelCache = secondLevelCache; + _jsonSettings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }; + } + + /// + /// Protected constructor + /// + /// First level memory cache + /// Second level distributed cache + /// + protected DistributedTwoLevelCache(IMemoryCache firstLevelCache, IDistributedCache secondLevelCache, bool clone) : this(firstLevelCache, secondLevelCache) + { + _clone = clone; + } + + /// + /// Gets the model with the specified key from cache. + /// + /// The model type + /// The unique key + /// The cached model, null it wasn't found + public T Get(string key) + { + //Check if the item exists in the first level cache + if (UseFirstLevelCache(key) && _firstLevelCache.TryGetValue(key, out var obj)) + { + if (!_clone) + { + return obj; + } + + if (obj != null) + { + return Utils.DeepClone(obj); + } + + return default(T); + } + + //The item didn't exist in the first level + //cache. Check in the second level cache + var json = _secondLevelCache.GetString(key); + + if (!string.IsNullOrEmpty(json)) + { + var ret = JsonConvert.DeserializeObject(json, _jsonSettings); + + //Update first level cache + SetUseFirstLevelCache(key, ret); + + return ret; + } + + //No item was found in first or second level cache + //Set first level cache to default(null) + SetUseFirstLevelCache(key, default(T)); + + return default(T); + } + + /// + /// Sets the given model in the cache. + /// + /// The model type + /// The unique key + /// The model + public void Set(string key, T value) + { + _secondLevelCache.SetString(key, JsonConvert.SerializeObject(value, _jsonSettings)); + + //Create a new cache version. This will "clear" the first + //level cache for all instances + CreateNewCacheVersion(); + + //Cache it in the first level cache + SetUseFirstLevelCache(key, value); + } + + /// + /// Removes the model with the specified key from cache. + /// + /// The unique key + public void Remove(string key) + { + _secondLevelCache.Remove(key); + + //Create a new cache version. This will "clear" the first + //level cache for all instances + CreateNewCacheVersion(); + + SetUseFirstLevelCache(key, null); + } + + /// + /// Invalidates first level cache + /// + public void InvalidateFirstLevelCache() + { + //Will cause that all first level cache items + //are invalid + _cacheVersion = null; + } + + private string CacheVersion + { + get + { + //If cacheversion is null this is the first time we check it for this request + //Then we need to get the current version from the distributed cache + //If no current version exists in the distributed cache we need to create it + if (_cacheVersion == null) + { + _cacheVersion = _secondLevelCache.GetString(CACHE_VERSION); + if (_cacheVersion == null) + { + CreateNewCacheVersion(); + } + } + return _cacheVersion; + } + } + + /// + /// Creates a new cache version This will "clear" the first + /// level cache. + /// + private void CreateNewCacheVersion() + { + _cacheVersion = Guid.NewGuid().ToString("N"); + _secondLevelCache.SetString(CACHE_VERSION, _cacheVersion); + } + + /// + /// Checks if first level cache should be used + /// + /// key + /// + private bool UseFirstLevelCache(string key) + { + _firstLevelCache.TryGetValue(GetFirstLevelCacheVersionKey(key), out var obj); + return obj != null && obj == "1"; + } + + /// + /// Sets that we should use the first level cache for the specified key + /// + /// key + private void SetUseFirstLevelCache(string key, object obj) + { + _firstLevelCache.Set(key, obj, new MemoryCacheEntryOptions() ); + _firstLevelCache.Set(GetFirstLevelCacheVersionKey(key), "1"); + } + + /// + /// Gets the first level cache key version + /// + /// + /// + private string GetFirstLevelCacheVersionKey(string key) + { + return key + "_" + CacheVersion; + } + + } + + +} diff --git a/core/Piranha/Cache/DistributedTwoLevelCacheWithClone.cs b/core/Piranha/Cache/DistributedTwoLevelCacheWithClone.cs new file mode 100644 index 000000000..8b54e832c --- /dev/null +++ b/core/Piranha/Cache/DistributedTwoLevelCacheWithClone.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) .NET Foundation and Contributors + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Text; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.Memory; +using Newtonsoft.Json; + +namespace Piranha.Cache +{ + /// + /// Two level distributed cache. The first level is a per application memory cache. + /// The second level is the distributed cache + /// + public class DistributedTwoLevelCacheWithClone : DistributedTwoLevelCache + { + protected DistributedTwoLevelCacheWithClone(IMemoryCache firstLevelCache, IDistributedCache secondLevelCache) : base(firstLevelCache, secondLevelCache, true) + { + } + } + + +} diff --git a/core/Piranha/Extensions/PiranhaDistributedCacheExtensions.cs b/core/Piranha/Extensions/PiranhaDistributedCacheExtensions.cs index 7c0ac1f0b..c580de9d5 100644 --- a/core/Piranha/Extensions/PiranhaDistributedCacheExtensions.cs +++ b/core/Piranha/Extensions/PiranhaDistributedCacheExtensions.cs @@ -11,6 +11,7 @@ using System; using System.Linq; using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Piranha; using Piranha.Cache; @@ -35,6 +36,33 @@ public static IServiceCollection AddPiranhaDistributedCache(this IServiceCollect return services.AddSingleton(); } + /// + /// Adds the distributed two level cache service for repository caching. + /// + /// The current service collection + /// + /// The updated service collection + public static IServiceCollection AddPiranhaDistributedTwoLevelCache(this IServiceCollection services, bool clone = false) + { + // Check dependent services + if (!services.Any(s => s.ServiceType == typeof(IDistributedCache))) + { + throw new NotSupportedException("You need to register a IDistributedCache service in order to use distributed two level cache in Piranha"); + } + + // Check dependent services + if (!services.Any(s => s.ServiceType == typeof(IMemoryCache))) + { + throw new NotSupportedException("You need to register a IMemoryCache service in order to use distributed two level cache in Piranha"); + } + + if (clone) + { + return services.AddScoped(); + } + return services.AddScoped(); + } + /// /// Uses the distributed cache service in the current application. /// @@ -47,4 +75,18 @@ public static PiranhaServiceBuilder UseDistributedCache(this PiranhaServiceBuild return serviceBuilder; } + + /// + /// Uses the distributed two level cache service in the current application. + /// + /// The current service builder + /// If returned objects should be cloned + /// The updated service builder + public static PiranhaServiceBuilder UseDistributedTwoLevelCache(this PiranhaServiceBuilder serviceBuilder, bool clone = false) + { + serviceBuilder.Services.AddPiranhaDistributedTwoLevelCache(clone); + + return serviceBuilder; + } + } \ No newline at end of file diff --git a/test/Piranha.Tests/DistributedTwoLevelCache.cs b/test/Piranha.Tests/DistributedTwoLevelCache.cs new file mode 100644 index 000000000..bfef61d83 --- /dev/null +++ b/test/Piranha.Tests/DistributedTwoLevelCache.cs @@ -0,0 +1,293 @@ +/* + * Copyright (c) .NET Foundation and Contributors + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + * + * http://github.com/piranhacms/piranha + * + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Piranha.Tests +{ + public class DistributedTwoLevelCache + { + + public DistributedTwoLevelCache() { + } + + [Fact] + public void SingleInstanceGetSetRemove() + { + var firstLevelCache = new MemoryCacheMock(); + var secondLevelCache = new DistributedCacheMock(); + var distributedCache = new Cache.DistributedTwoLevelCache(firstLevelCache, secondLevelCache); + + var key = "id"; + + //Set item for first time + //This will cause 2 hits on second level cache + distributedCache.Set(key, "ver"); + Assert.Equal(2, secondLevelCache.Hits); + + //Get item from cache. The first level + //cache will be used. No hits on the second level + //cache + Assert.Equal("ver", distributedCache.Get(key)); + Assert.Equal(2, secondLevelCache.Hits); + + //Update the item + //This should cause 2 hits on the second level + //cache + distributedCache.Set(key, "ver2"); + Assert.Equal(4, secondLevelCache.Hits); + + //Get item from cache again once + //No second level cache hits + Assert.Equal("ver2", distributedCache.Get(key)); + Assert.Equal(4, secondLevelCache.Hits); + + //Get item from cache again twice + //No second level cache hits + Assert.Equal("ver2", distributedCache.Get(key)); + Assert.Equal(4, secondLevelCache.Hits); + + //Remove item + //This will cause 2 hits on second level cache + distributedCache.Remove(key); + Assert.Equal(6, secondLevelCache.Hits); + + //Get removed item from cache + //No second level cache hits + Assert.Null(distributedCache.Get(key)); + Assert.Equal(6, secondLevelCache.Hits); + + } + + [Fact] + public void TwoInstanceGetSetRemove() + { + var secondLevelCache = new DistributedCacheMock(); + var firstLevelCache1 = new MemoryCacheMock(); + var firstLevelCache2 = new MemoryCacheMock(); + var instance1 = new Cache.DistributedTwoLevelCache(firstLevelCache1, secondLevelCache); + var instance2 = new Cache.DistributedTwoLevelCache(firstLevelCache2, secondLevelCache); + + //Test item key + var key = "id"; + + //Set item from instance 1 + //2 hits on second level cache + instance1.Set(key, "ver"); + Assert.Equal(2, secondLevelCache.Hits); + + //Item should be in instance 1 first level cache + //No hits on second level cache + Assert.Equal("ver", instance1.Get(key)); + Assert.Equal(2, secondLevelCache.Hits); + + //Item should be fetched into instance 2 first level + //cache + //2 hits on second level cache + Assert.Equal("ver", instance2.Get(key)); + Assert.Equal(4, secondLevelCache.Hits); + + //Update item from instance 1 + //2 hits on second level cache + instance1.Set(key, "ver2"); + Assert.Equal(6, secondLevelCache.Hits); + + //Instance 1 should have the correct item, because it set + //the new item + //No hits on second level cache + Assert.Equal("ver2", instance1.Get(key)); + Assert.Equal(6, secondLevelCache.Hits); + + //Instance 2 should not have the correct item yet, because + //the firstlevel cache has not been cleared. In a prod + //scenario the first level cache will check the cache version + //in the next request + //No hits on second level cache + Assert.Equal("ver", instance2.Get(key)); + Assert.Equal(6, secondLevelCache.Hits); + + //But if we clear instance 2's first level cache it should + //get the new item + //2 hits on second level cache + instance2.InvalidateFirstLevelCache(); + Assert.Equal("ver2", instance2.Get(key)); + Assert.Equal(8, secondLevelCache.Hits); + + //Instance 1 removes the item + //2 hits on second level cache + instance1.Remove(key); + Assert.Equal(10, secondLevelCache.Hits); + + //Instance 1 first level cache should have been updated + //No hits on second level cache + Assert.Null(instance1.Get(key)); + Assert.Equal(10, secondLevelCache.Hits); + + //Instance 2 first level cache should not have + //been updated. In a prod scenario the first level + //cache will check the cache version in the next request + //No hits on second level cache + Assert.NotNull(instance2.Get(key)); + + //If we clear instance 2 first level cache the + //item should have been removed + //2 hits on second level cache + instance2.InvalidateFirstLevelCache(); + Assert.Null(instance2.Get(key)); + Assert.Equal(12, secondLevelCache.Hits); + } + + #region Mocks + private class DistributedCacheMock : IDistributedCache + { + + public int Gets { get; set; } = 0; + public int Sets { get; set; } = 0; + public int Removes { get; set; } = 0; + public int Hits => Gets + Sets + Removes; + public readonly Dictionary Cache = new(); + + public byte[] Get(string key) + { + Gets++; + if (Cache.TryGetValue(key, out byte[] data)) + { + return data; + } + return null; + } + + public Task GetAsync(string key, CancellationToken token = new CancellationToken()) + { + throw new NotImplementedException(); + } + + public void Set(string key, byte[] value, DistributedCacheEntryOptions options) + { + Sets++; + Cache[key] = value; + } + + public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, + CancellationToken token = new CancellationToken()) + { + throw new NotImplementedException(); + } + + public void Refresh(string key) + { + throw new NotImplementedException(); + } + + public Task RefreshAsync(string key, CancellationToken token = new CancellationToken()) + { + throw new NotImplementedException(); + } + + public void Remove(string key) + { + Removes++; + if (Cache.ContainsKey(key)) + { + Cache.Remove(key); + } + } + + public Task RemoveAsync(string key, CancellationToken token = new CancellationToken()) + { + throw new NotImplementedException(); + } + } + + private class MemoryCacheMock : IMemoryCache + { + public IList CacheEntries = new List(); + public int Gets { get; set; } = 0; + public int Sets { get; set; } = 0; + public int Removes { get; set; } = 0; + public int Total => Gets + Sets + Removes; + + public MemoryCacheMock() + { + } + + public void Dispose() + { + } + + public bool TryGetValue(object key, out object value) + { + Gets++; + var tmp = CacheEntries.SingleOrDefault(o => o.Key.ToString() == key.ToString()); + if (tmp != null) + { + value = tmp.Value; + return true; + } + + value = null; + return false; + } + + public ICacheEntry CreateEntry(object key) + { + Sets++; + var tmp = CacheEntries.SingleOrDefault(o => o.Key.ToString() == key.ToString()); + + if (tmp == null) + { + tmp = new CacheEntryMock() + { + Key = key + }; + CacheEntries.Add(tmp); + } + + return tmp; + } + + public void Remove(object key) + { + Removes++; + CacheEntries = CacheEntries.Where(o => o.Key.ToString() != key.ToString()).ToList(); + } + + } + + private class CacheEntryMock : ICacheEntry + { + public void Dispose() + { + } + + public object Key { get; set; } + public object Value { get; set; } + public DateTimeOffset? AbsoluteExpiration { get; set; } + public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; } + public TimeSpan? SlidingExpiration { get; set; } + public IList ExpirationTokens { get; } + public IList PostEvictionCallbacks { get; } + public CacheItemPriority Priority { get; set; } + public long? Size { get; set; } + } + + #endregion + + } +} \ No newline at end of file diff --git a/test/Piranha.Tests/Services/AliasTests.cs b/test/Piranha.Tests/Services/AliasTests.cs index e7565a9c8..24e758fa0 100644 --- a/test/Piranha.Tests/Services/AliasTests.cs +++ b/test/Piranha.Tests/Services/AliasTests.cs @@ -38,6 +38,17 @@ public override Task InitializeAsync() } } + + [Collection("Integration tests")] + public class AliasTestsDistributedTwoLevelCache : AliasTests + { + public override Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + return base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class AliasTests : BaseTestsAsync { @@ -110,7 +121,9 @@ public void IsCached() { using (var api = CreateApi()) { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(AliasTestsMemoryCache) || - this.GetType() == typeof(AliasTestsDistributedCache)); + this.GetType() == typeof(AliasTestsDistributedCache) || + this.GetType() == typeof(AliasTestsDistributedTwoLevelCache) + ); } } diff --git a/test/Piranha.Tests/Services/CommentTests.cs b/test/Piranha.Tests/Services/CommentTests.cs index 6b5b79472..c83cbd053 100644 --- a/test/Piranha.Tests/Services/CommentTests.cs +++ b/test/Piranha.Tests/Services/CommentTests.cs @@ -40,6 +40,16 @@ public override Task InitializeAsync() } } + [Collection("Integration tests")] + public class CommentTestsDistributedTwoLevelCache : CommentTests + { + public override Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + return base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class CommentTests : BaseTestsAsync { @@ -151,6 +161,7 @@ public void IsCached() { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(CommentTestsMemoryCache) || + this.GetType() == typeof(CommentTestsDistributedTwoLevelCache) || this.GetType() == typeof(CommentTestsDistributedCache)); } } diff --git a/test/Piranha.Tests/Services/ContentTests.cs b/test/Piranha.Tests/Services/ContentTests.cs index eb27ea50d..b0570b733 100644 --- a/test/Piranha.Tests/Services/ContentTests.cs +++ b/test/Piranha.Tests/Services/ContentTests.cs @@ -40,6 +40,16 @@ public override async Task InitializeAsync() } } + [Collection("Integration tests")] + public class ContentTestsDistributedTwoLevelCache : ContentTests + { + public override async Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + await base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class ContentTests : BaseTestsAsync { diff --git a/test/Piranha.Tests/Services/MediaTests.cs b/test/Piranha.Tests/Services/MediaTests.cs index 29b058399..18dab8314 100644 --- a/test/Piranha.Tests/Services/MediaTests.cs +++ b/test/Piranha.Tests/Services/MediaTests.cs @@ -39,6 +39,16 @@ public override Task InitializeAsync() } } + [Collection("Integration tests")] + public class MediaTestsDistributedTwoLevelCache : MediaTests + { + public override Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + return base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class MediaTests : BaseTestsAsync { @@ -157,6 +167,7 @@ public void IsCached() { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(MediaTestsMemoryCache) || + this.GetType() == typeof(MediaTestsDistributedTwoLevelCache) || this.GetType() == typeof(MediaTestsDistributedCache)); } } diff --git a/test/Piranha.Tests/Services/PageTests.cs b/test/Piranha.Tests/Services/PageTests.cs index 1e8a9e9f9..caea58d7f 100644 --- a/test/Piranha.Tests/Services/PageTests.cs +++ b/test/Piranha.Tests/Services/PageTests.cs @@ -44,6 +44,16 @@ public override async Task InitializeAsync() } } + [Collection("Integration tests")] + public class PageTestsDistributedTwoLevelCache : PageTests + { + public override async Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + await base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class PageTests : BaseTestsAsync { @@ -287,6 +297,7 @@ public void IsCached() { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(PageTestsMemoryCache) || + this.GetType() == typeof(PageTestsDistributedTwoLevelCache) || this.GetType() == typeof(PageTestsDistributedCache)); } } diff --git a/test/Piranha.Tests/Services/PageTypeTests.cs b/test/Piranha.Tests/Services/PageTypeTests.cs index 796f26428..1b008f439 100644 --- a/test/Piranha.Tests/Services/PageTypeTests.cs +++ b/test/Piranha.Tests/Services/PageTypeTests.cs @@ -37,6 +37,16 @@ public override Task InitializeAsync() } } + [Collection("Integration tests")] + public class PageTypeTestsDistributedTwoLevelCache : PageTypeTests + { + public override Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + return base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class PageTypeTests : BaseTestsAsync { @@ -169,6 +179,7 @@ public void IsCached() { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(PageTypeTestsMemoryCache) || + this.GetType() == typeof(PageTypeTestsDistributedTwoLevelCache) || this.GetType() == typeof(PageTypeTestsDistributedCache)); } } diff --git a/test/Piranha.Tests/Services/ParamTests.cs b/test/Piranha.Tests/Services/ParamTests.cs index c53f4455f..b1a6bd3ff 100644 --- a/test/Piranha.Tests/Services/ParamTests.cs +++ b/test/Piranha.Tests/Services/ParamTests.cs @@ -38,6 +38,16 @@ public override Task InitializeAsync() } } + [Collection("Integration tests")] + public class ParamTestsDistributedTwoLevelCache : ParamTests + { + public override Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + return base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class ParamTests : BaseTestsAsync { @@ -91,6 +101,7 @@ public void IsCached() { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(ParamTestsMemoryCache) || + this.GetType() == typeof(ParamTestsDistributedTwoLevelCache) || this.GetType() == typeof(ParamTestsDistributedCache)); } } diff --git a/test/Piranha.Tests/Services/PostTests.cs b/test/Piranha.Tests/Services/PostTests.cs index 5176e4d6b..28542e16b 100644 --- a/test/Piranha.Tests/Services/PostTests.cs +++ b/test/Piranha.Tests/Services/PostTests.cs @@ -44,6 +44,16 @@ public override async Task InitializeAsync() } } + [Collection("Integration tests")] + public class PostTestsDistributedTwoLevelCache : PostTests + { + public override async Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + await base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class PostTests : BaseTestsAsync { @@ -249,6 +259,7 @@ public void IsCached() { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(PostTestsMemoryCache) || + this.GetType() == typeof(PostTestsDistributedTwoLevelCache) || this.GetType() == typeof(PostTestsDistributedCache)); } } diff --git a/test/Piranha.Tests/Services/PostTypeTests.cs b/test/Piranha.Tests/Services/PostTypeTests.cs index 5339c3710..6622582bd 100644 --- a/test/Piranha.Tests/Services/PostTypeTests.cs +++ b/test/Piranha.Tests/Services/PostTypeTests.cs @@ -37,6 +37,16 @@ public override Task InitializeAsync() } } + [Collection("Integration tests")] + public class PostTypeTestsDistributedTwoLevelCache : PostTypeTests + { + public override Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + return base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class PostTypeTests : BaseTestsAsync { @@ -169,6 +179,7 @@ public void IsCached() { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(PostTypeTestsMemoryCache) || + this.GetType() == typeof(PostTypeTestsDistributedTwoLevelCache) || this.GetType() == typeof(PostTypeTestsDistributedCache)); } } diff --git a/test/Piranha.Tests/Services/SiteTests.cs b/test/Piranha.Tests/Services/SiteTests.cs index 494de4fdf..781e669ff 100644 --- a/test/Piranha.Tests/Services/SiteTests.cs +++ b/test/Piranha.Tests/Services/SiteTests.cs @@ -42,6 +42,16 @@ public override async Task InitializeAsync() } } + [Collection("Integration tests")] + public class SiteTestsDistributedTwoLevelCache : SiteTests + { + public override async Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + await base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class SiteTests : BaseTestsAsync { @@ -198,6 +208,7 @@ public void IsCached() using (var api = CreateApi()) { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(SiteTestsMemoryCache) || + this.GetType() == typeof(SiteTestsDistributedTwoLevelCache) || this.GetType() == typeof(SiteTestsDistributedCache)); } } diff --git a/test/Piranha.Tests/Services/SiteTypeTests.cs b/test/Piranha.Tests/Services/SiteTypeTests.cs index 04e9a3c84..a31ae6619 100644 --- a/test/Piranha.Tests/Services/SiteTypeTests.cs +++ b/test/Piranha.Tests/Services/SiteTypeTests.cs @@ -37,6 +37,16 @@ public override Task InitializeAsync() } } + [Collection("Integration tests")] + public class SiteTypeTestsDistributedTwoLevelCache : SiteTypeTests + { + public override Task InitializeAsync() + { + _cache = new Cache.DistributedTwoLevelCache((IMemoryCache)_services.GetService(typeof(IMemoryCache)), (IDistributedCache)_services.GetService(typeof(IDistributedCache))); + return base.InitializeAsync(); + } + } + [Collection("Integration tests")] public class SiteTypeTests : BaseTestsAsync { @@ -168,6 +178,7 @@ public void IsCached() { Assert.Equal(((Api)api).IsCached, this.GetType() == typeof(SiteTypeTestsMemoryCache) || + this.GetType() == typeof(SiteTypeTestsDistributedTwoLevelCache) || this.GetType() == typeof(SiteTypeTestsDistributedCache)); } }