diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentDataObject.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentDataObject.cs index 813cc2c1a..148434072 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentDataObject.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentDataObject.cs @@ -13,7 +13,6 @@ using Jint.Native; using Jint.Native.Object; using Jint.Runtime; using Jint.Runtime.Descriptors; -using Orleans; using Squidex.Domain.Apps.Core.Contents; using Squidex.Infrastructure; diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryReferrersAsync.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryReferrersAsync.cs index 7456f4b53..cd2665146 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryReferrersAsync.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryReferrersAsync.cs @@ -6,8 +6,6 @@ // ========================================================================== using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; diff --git a/backend/src/Squidex.Domain.Apps.Entities/AppProvider.cs b/backend/src/Squidex.Domain.Apps.Entities/AppProvider.cs index dc51bfc54..ef760f710 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/AppProvider.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/AppProvider.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Squidex.Caching; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Indexes; using Squidex.Domain.Apps.Entities.Rules; @@ -16,7 +17,6 @@ using Squidex.Domain.Apps.Entities.Rules.Indexes; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Indexes; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Security; namespace Squidex.Domain.Apps.Entities diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs index 7cd694b26..cb0b65f26 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs @@ -10,9 +10,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Orleans; +using Squidex.Caching; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Orleans; @@ -36,7 +36,6 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes Guard.NotNull(replicatedCache, nameof(replicatedCache)); this.grainFactory = grainFactory; - this.replicatedCache = replicatedCache; } @@ -149,7 +148,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes if (app != null) { - CacheIt(app, false); + await CacheItAsync(app, false); } return app; @@ -227,7 +226,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes if (app != null) { - CacheIt(app, true); + await CacheItAsync(app, true); switch (context.Command) { @@ -319,10 +318,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes return $"APPS_NAME_{name}"; } - private void CacheIt(IAppEntity app, bool publish) + private Task CacheItAsync(IAppEntity app, bool publish) { - replicatedCache.Add(GetCacheKey(app.Id), app, CacheDuration, publish); - replicatedCache.Add(GetCacheKey(app.Name), app, CacheDuration, publish); + return Task.WhenAll( + replicatedCache.AddAsync(GetCacheKey(app.Id), app, CacheDuration, publish), + replicatedCache.AddAsync(GetCacheKey(app.Name), app, CacheDuration, publish)); } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs index 5908d4174..48c3a669a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs @@ -16,11 +16,9 @@ using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Contents.Commands; -using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Log; -using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Validation; #pragma warning disable IDE0016 // Use 'throw' expression diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs index 51f687712..10f58739b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs @@ -15,20 +15,21 @@ using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Assets; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { - public sealed class CachingGraphQLService : CachingProviderBase, IGraphQLService + public sealed class CachingGraphQLService : IGraphQLService { private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10); + private readonly IMemoryCache cache; private readonly IServiceProvider resolver; public CachingGraphQLService(IMemoryCache cache, IServiceProvider resolver) - : base(cache) { + Guard.NotNull(cache, nameof(cache)); Guard.NotNull(resolver, nameof(resolver)); + this.cache = cache; this.resolver = resolver; } @@ -83,7 +84,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { var cacheKey = CreateCacheKey(app.Id, app.Version.ToString()); - return Cache.GetOrCreateAsync(cacheKey, async entry => + return cache.GetOrCreateAsync(cacheKey, async entry => { entry.AbsoluteExpirationRelativeToNow = CacheDuration; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs index 48f404124..c73fb238d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs @@ -20,7 +20,6 @@ using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Log; @@ -33,20 +32,21 @@ using Squidex.Text; namespace Squidex.Domain.Apps.Entities.Contents.Queries { - public class ContentQueryParser : CachingProviderBase + public class ContentQueryParser { private static readonly TimeSpan CacheTime = TimeSpan.FromMinutes(60); + private readonly IMemoryCache cache; private readonly IJsonSerializer jsonSerializer; private readonly ContentOptions options; public ContentQueryParser(IMemoryCache cache, IJsonSerializer jsonSerializer, IOptions options) - : base(cache) { Guard.NotNull(jsonSerializer, nameof(jsonSerializer)); + Guard.NotNull(cache, nameof(cache)); Guard.NotNull(options, nameof(options)); this.jsonSerializer = jsonSerializer; - + this.cache = cache; this.options = options.Value; } @@ -138,7 +138,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries { var cacheKey = BuildJsonCacheKey(context.App, schema, context.IsFrontendClient); - var result = Cache.GetOrCreate(cacheKey, entry => + var result = cache.GetOrCreate(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = CacheTime; @@ -152,7 +152,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries { var cacheKey = BuildEmdCacheKey(context.App, schema, context.IsFrontendClient); - var result = Cache.GetOrCreate(cacheKey, entry => + var result = cache.GetOrCreate(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = CacheTime; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/State/CachingTextIndexerState.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/State/CachingTextIndexerState.cs index e9dacb3be..2bb1a874f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/State/CachingTextIndexerState.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/State/CachingTextIndexerState.cs @@ -8,8 +8,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Squidex.Caching; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; namespace Squidex.Domain.Apps.Entities.Contents.Text.State { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs index fc01ca334..3335bbeb1 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs @@ -9,12 +9,12 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; +using Squidex.Caching; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Entities.Rules diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs index a9a1646ab..2a84f7fc3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs @@ -10,9 +10,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Orleans; +using Squidex.Caching; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Translations; @@ -33,7 +33,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes Guard.NotNull(replicatedCache, nameof(replicatedCache)); this.grainFactory = grainFactory; - this.replicatedCache = replicatedCache; } @@ -99,7 +98,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes if (schema != null) { - CacheIt(schema, false); + await CacheItAsync(schema, false); } return schema; @@ -159,7 +158,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes if (schema != null) { - CacheIt(schema, true); + await CacheItAsync(schema, true); if (context.Command is DeleteSchema) { @@ -221,10 +220,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes return $"SCHEMAS_ID_{appId}_{id}"; } - private void CacheIt(ISchemaEntity schema, bool publish) + private Task CacheItAsync(ISchemaEntity schema, bool publish) { - replicatedCache.Add(GetCacheKey(schema.AppId.Id, schema.Id), schema, CacheDuration, publish); - replicatedCache.Add(GetCacheKey(schema.AppId.Id, schema.SchemaDef.Name), schema, CacheDuration, publish); + return Task.WhenAll( + replicatedCache.AddAsync(GetCacheKey(schema.AppId.Id, schema.Id), schema, CacheDuration, publish), + replicatedCache.AddAsync(GetCacheKey(schema.AppId.Id, schema.SchemaDef.Name), schema, CacheDuration, publish)); } } } diff --git a/backend/src/Squidex.Infrastructure.Amazon/Squidex.Infrastructure.Amazon.csproj b/backend/src/Squidex.Infrastructure.Amazon/Squidex.Infrastructure.Amazon.csproj index 6ab072ae1..ad0e92c2b 100644 --- a/backend/src/Squidex.Infrastructure.Amazon/Squidex.Infrastructure.Amazon.csproj +++ b/backend/src/Squidex.Infrastructure.Amazon/Squidex.Infrastructure.Amazon.csproj @@ -6,7 +6,7 @@ enable - + diff --git a/backend/src/Squidex.Infrastructure/Caching/AsyncLocalCache.cs b/backend/src/Squidex.Infrastructure/Caching/AsyncLocalCache.cs deleted file mode 100644 index 9a7611c10..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/AsyncLocalCache.cs +++ /dev/null @@ -1,79 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections.Concurrent; -using System.Threading; -using Squidex.Infrastructure.Tasks; - -#pragma warning disable CS8601 // Possible null reference assignment. - -namespace Squidex.Infrastructure.Caching -{ - public sealed class AsyncLocalCache : ILocalCache - { - private static readonly AsyncLocal> LocalCache = new AsyncLocal>(); - private static readonly AsyncLocalCleaner> Cleaner; - - static AsyncLocalCache() - { - Cleaner = new AsyncLocalCleaner>(LocalCache); - } - - public IDisposable StartContext() - { - LocalCache.Value = new ConcurrentDictionary(); - - return Cleaner; - } - - public void Add(object key, object? value) - { - var cacheKey = GetCacheKey(key); - - var cache = LocalCache.Value; - - if (cache != null) - { - cache[cacheKey] = value; - } - } - - public void Remove(object key) - { - var cacheKey = GetCacheKey(key); - - var cache = LocalCache.Value; - - if (cache != null) - { - cache.TryRemove(cacheKey, out _); - } - } - - public bool TryGetValue(object key, out object? value) - { - var cacheKey = GetCacheKey(key); - - var cache = LocalCache.Value; - - if (cache != null) - { - return cache.TryGetValue(cacheKey, out value); - } - - value = null; - - return false; - } - - private static string GetCacheKey(object key) - { - return $"CACHE_{key}"; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Caching/CachingProviderBase.cs b/backend/src/Squidex.Infrastructure/Caching/CachingProviderBase.cs deleted file mode 100644 index 9ff773e51..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/CachingProviderBase.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Microsoft.Extensions.Caching.Memory; - -namespace Squidex.Infrastructure.Caching -{ - public abstract class CachingProviderBase - { - private readonly IMemoryCache cache; - - protected IMemoryCache Cache - { - get { return cache; } - } - - protected CachingProviderBase(IMemoryCache cache) - { - Guard.NotNull(cache, nameof(cache)); - - this.cache = cache; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Caching/ILocalCache.cs b/backend/src/Squidex.Infrastructure/Caching/ILocalCache.cs deleted file mode 100644 index 8be615ca3..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/ILocalCache.cs +++ /dev/null @@ -1,22 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; - -namespace Squidex.Infrastructure.Caching -{ - public interface ILocalCache - { - IDisposable StartContext(); - - void Add(object key, object? value); - - void Remove(object key); - - bool TryGetValue(object key, out object? value); - } -} diff --git a/backend/src/Squidex.Infrastructure/Caching/IPubSub.cs b/backend/src/Squidex.Infrastructure/Caching/IPubSub.cs deleted file mode 100644 index a2dbad92e..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/IPubSub.cs +++ /dev/null @@ -1,18 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; - -namespace Squidex.Infrastructure.Caching -{ - public interface IPubSub - { - void Publish(object message); - - void Subscribe(Action handler); - } -} diff --git a/backend/src/Squidex.Infrastructure/Caching/IPubSubSubscription.cs b/backend/src/Squidex.Infrastructure/Caching/IPubSubSubscription.cs deleted file mode 100644 index c735e6d08..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/IPubSubSubscription.cs +++ /dev/null @@ -1,13 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Infrastructure.Caching -{ - internal interface IPubSubSubscription - { - } -} \ No newline at end of file diff --git a/backend/src/Squidex.Infrastructure/Caching/IReplicatedCache.cs b/backend/src/Squidex.Infrastructure/Caching/IReplicatedCache.cs deleted file mode 100644 index 075e515f9..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/IReplicatedCache.cs +++ /dev/null @@ -1,20 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; - -namespace Squidex.Infrastructure.Caching -{ - public interface IReplicatedCache - { - void Add(string key, object? value, TimeSpan expiration, bool invalidate); - - void Remove(string key); - - bool TryGetValue(string key, out object? value); - } -} diff --git a/backend/src/Squidex.Infrastructure/Caching/LRUCache.cs b/backend/src/Squidex.Infrastructure/Caching/LRUCache.cs deleted file mode 100644 index cfb1236d5..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/LRUCache.cs +++ /dev/null @@ -1,123 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace Squidex.Infrastructure.Caching -{ - public sealed class LRUCache where TKey : notnull - { - private readonly Dictionary>> cacheMap = new Dictionary>>(); - private readonly LinkedList> cacheHistory = new LinkedList>(); - private readonly int capacity; - private readonly Action itemEvicted; - - public int Count - { - get { return cacheMap.Count; } - } - - public IEnumerable Keys - { - get { return cacheMap.Keys; } - } - - public LRUCache(int capacity, Action? itemEvicted = null) - { - Guard.GreaterThan(capacity, 0, nameof(capacity)); - - this.capacity = capacity; - - this.itemEvicted = itemEvicted ?? ((key, value) => { }); - } - - public void Clear() - { - cacheHistory.Clear(); - cacheMap.Clear(); - } - - public bool Set(TKey key, TValue value) - { - if (cacheMap.TryGetValue(key, out var node)) - { - node.Value.Value = value; - - cacheHistory.Remove(node); - cacheHistory.AddLast(node); - - cacheMap[key] = node; - - return true; - } - - if (cacheMap.Count >= capacity) - { - RemoveFirst(); - } - - var cacheItem = new LRUCacheItem { Key = key, Value = value }; - - node = new LinkedListNode>(cacheItem); - - cacheMap.Add(key, node); - cacheHistory.AddLast(node); - - return false; - } - - public bool Remove(TKey key) - { - if (cacheMap.TryGetValue(key, out var node)) - { - cacheMap.Remove(key); - cacheHistory.Remove(node); - - return true; - } - - return false; - } - - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) - { - value = default!; - - if (cacheMap.TryGetValue(key, out var node)) - { - value = node.Value.Value; - - cacheHistory.Remove(node); - cacheHistory.AddLast(node); - - return true; - } - - return false; - } - - public bool Contains(TKey key) - { - return cacheMap.ContainsKey(key); - } - - private void RemoveFirst() - { - var node = cacheHistory.First; - - if (node != null) - { - itemEvicted(node.Value.Key, node.Value.Value); - - cacheMap.Remove(node.Value.Key); - cacheHistory.RemoveFirst(); - } - } - } -} \ No newline at end of file diff --git a/backend/src/Squidex.Infrastructure/Caching/LRUCacheItem.cs b/backend/src/Squidex.Infrastructure/Caching/LRUCacheItem.cs deleted file mode 100644 index 3a6b20ef4..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/LRUCacheItem.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -#pragma warning disable SA1401 // Fields must be private -#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. - -namespace Squidex.Infrastructure.Caching -{ - internal class LRUCacheItem - { - public TKey Key; - - public TValue Value; - } -} \ No newline at end of file diff --git a/backend/src/Squidex.Infrastructure/Caching/LocalCacheExtensions.cs b/backend/src/Squidex.Infrastructure/Caching/LocalCacheExtensions.cs deleted file mode 100644 index aa50178bb..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/LocalCacheExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Threading.Tasks; - -namespace Squidex.Infrastructure.Caching -{ - public static class LocalCacheExtensions - { - public static async Task GetOrCreateAsync(this ILocalCache cache, object key, Func> task) - { - if (cache.TryGetValue(key, out var value)) - { - if (value is T typed) - { - return typed; - } - else - { - return default!; - } - } - - var result = await task(); - - cache.Add(key, result); - - return result; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Caching/ReplicatedCache.cs b/backend/src/Squidex.Infrastructure/Caching/ReplicatedCache.cs deleted file mode 100644 index 81c87e7ba..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/ReplicatedCache.cs +++ /dev/null @@ -1,103 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Options; - -namespace Squidex.Infrastructure.Caching -{ - public sealed class ReplicatedCache : IReplicatedCache - { - private readonly Guid instanceId = Guid.NewGuid(); - private readonly IMemoryCache memoryCache; - private readonly IPubSub pubSub; - private readonly ReplicatedCacheOptions options; - - public class InvalidateMessage - { - public Guid Source { get; set; } - - public string Key { get; set; } - } - - public ReplicatedCache(IMemoryCache memoryCache, IPubSub pubSub, IOptions options) - { - Guard.NotNull(memoryCache, nameof(memoryCache)); - Guard.NotNull(pubSub, nameof(pubSub)); - Guard.NotNull(options, nameof(options)); - - this.memoryCache = memoryCache; - - this.pubSub = pubSub; - - if (options.Value.Enable) - { - this.pubSub.Subscribe(OnMessage); - } - - this.options = options.Value; - } - - private void OnMessage(object message) - { - if (message is InvalidateMessage invalidate && invalidate.Source != instanceId) - { - memoryCache.Remove(invalidate.Key); - } - } - - public void Add(string key, object? value, TimeSpan expiration, bool invalidate) - { - if (!options.Enable) - { - return; - } - - memoryCache.Set(key, value, expiration); - - if (invalidate) - { - Invalidate(key); - } - } - - public void Remove(string key) - { - if (!options.Enable) - { - return; - } - - memoryCache.Remove(key); - - Invalidate(key); - } - - public bool TryGetValue(string key, out object? value) - { - if (!options.Enable) - { - value = null; - - return false; - } - - return memoryCache.TryGetValue(key, out value); - } - - private void Invalidate(string key) - { - if (!options.Enable) - { - return; - } - - pubSub.Publish(new InvalidateMessage { Key = key, Source = instanceId }); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Caching/ReplicatedCacheOptions.cs b/backend/src/Squidex.Infrastructure/Caching/ReplicatedCacheOptions.cs deleted file mode 100644 index d1d1d9f48..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/ReplicatedCacheOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Infrastructure.Caching -{ - public sealed class ReplicatedCacheOptions - { - public bool Enable { get; set; } - } -} diff --git a/backend/src/Squidex.Infrastructure/Caching/SimplePubSub.cs b/backend/src/Squidex.Infrastructure/Caching/SimplePubSub.cs deleted file mode 100644 index 21383ee0d..000000000 --- a/backend/src/Squidex.Infrastructure/Caching/SimplePubSub.cs +++ /dev/null @@ -1,32 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections.Generic; - -namespace Squidex.Infrastructure.Caching -{ - public class SimplePubSub : IPubSub - { - private readonly List> handlers = new List>(); - - public virtual void Publish(object message) - { - foreach (var handler in handlers) - { - handler(message); - } - } - - public virtual void Subscribe(Action handler) - { - Guard.NotNull(handler, nameof(handler)); - - handlers.Add(handler); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs b/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs index 433afcd60..3c41d36e9 100644 --- a/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs +++ b/backend/src/Squidex.Infrastructure/Commands/Rebuilder.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; -using Squidex.Infrastructure.Caching; +using Squidex.Caching; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.States; diff --git a/backend/src/Squidex.Infrastructure/Orleans/ActivationLimiter.cs b/backend/src/Squidex.Infrastructure/Orleans/ActivationLimiter.cs index af4f44b2e..0d2367c0f 100644 --- a/backend/src/Squidex.Infrastructure/Orleans/ActivationLimiter.cs +++ b/backend/src/Squidex.Infrastructure/Orleans/ActivationLimiter.cs @@ -8,7 +8,7 @@ using System; using System.Collections.Concurrent; using System.Threading; -using Squidex.Infrastructure.Caching; +using Squidex.Caching; namespace Squidex.Infrastructure.Orleans { diff --git a/backend/src/Squidex.Infrastructure/Orleans/IPubSubGrain.cs b/backend/src/Squidex.Infrastructure/Orleans/IPubSubGrain.cs deleted file mode 100644 index 3c45bd87d..000000000 --- a/backend/src/Squidex.Infrastructure/Orleans/IPubSubGrain.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Threading.Tasks; -using Orleans; - -namespace Squidex.Infrastructure.Orleans -{ - public interface IPubSubGrain : IGrainWithStringKey - { - Task SubscribeAsync(IPubSubGrainObserver observer); - - Task PublishAsync(object message); - } -} diff --git a/backend/src/Squidex.Infrastructure/Orleans/IPubSubGrainObserver.cs b/backend/src/Squidex.Infrastructure/Orleans/IPubSubGrainObserver.cs deleted file mode 100644 index a7ea60e54..000000000 --- a/backend/src/Squidex.Infrastructure/Orleans/IPubSubGrainObserver.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Orleans; - -namespace Squidex.Infrastructure.Orleans -{ - public interface IPubSubGrainObserver : IGrainObserver - { - void Handle(object message); - - void Subscribe(Action handler); - } -} diff --git a/backend/src/Squidex.Infrastructure/Orleans/LocalCacheFilter.cs b/backend/src/Squidex.Infrastructure/Orleans/LocalCacheFilter.cs index 6e7dec26a..e839eeb37 100644 --- a/backend/src/Squidex.Infrastructure/Orleans/LocalCacheFilter.cs +++ b/backend/src/Squidex.Infrastructure/Orleans/LocalCacheFilter.cs @@ -8,7 +8,7 @@ using System; using System.Threading.Tasks; using Orleans; -using Squidex.Infrastructure.Caching; +using Squidex.Caching; namespace Squidex.Infrastructure.Orleans { diff --git a/backend/src/Squidex.Infrastructure/Orleans/OrleansPubSub.cs b/backend/src/Squidex.Infrastructure/Orleans/OrleansPubSub.cs deleted file mode 100644 index a222c09d9..000000000 --- a/backend/src/Squidex.Infrastructure/Orleans/OrleansPubSub.cs +++ /dev/null @@ -1,79 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Orleans; -using Squidex.Infrastructure.Caching; -using Squidex.Infrastructure.Tasks; - -namespace Squidex.Infrastructure.Orleans -{ - public sealed class OrleansPubSub : IBackgroundProcess, IPubSub - { - private readonly IPubSubGrain pubSubGrain; - private readonly IPubSubGrainObserver pubSubGrainObserver = new Observer(); - private readonly IGrainFactory grainFactory; - - private sealed class Observer : IPubSubGrainObserver - { - private readonly List> subscriptions = new List>(); - - public void Handle(object message) - { - foreach (var subscription in subscriptions) - { - try - { - subscription(message); - } - catch - { - continue; - } - } - } - - public void Subscribe(Action handler) - { - subscriptions.Add(handler); - } - } - - public OrleansPubSub(IGrainFactory grainFactory) - { - Guard.NotNull(grainFactory, nameof(grainFactory)); - - this.grainFactory = grainFactory; - - pubSubGrain = grainFactory.GetGrain(SingleGrain.Id); - } - - public async Task StartAsync(CancellationToken ct) - { - var reference = await grainFactory.CreateObjectReference(pubSubGrainObserver); - - await pubSubGrain.SubscribeAsync(reference); - } - - public void Publish(object message) - { - Guard.NotNull(message, nameof(message)); - - pubSubGrain.PublishAsync(message).Forget(); - } - - public void Subscribe(Action handler) - { - Guard.NotNull(handler, nameof(handler)); - - pubSubGrainObserver.Subscribe(handler); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Orleans/OrleansPubSubGrain.cs b/backend/src/Squidex.Infrastructure/Orleans/OrleansPubSubGrain.cs deleted file mode 100644 index 776287d13..000000000 --- a/backend/src/Squidex.Infrastructure/Orleans/OrleansPubSubGrain.cs +++ /dev/null @@ -1,42 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using System.Threading.Tasks; -using Orleans; - -namespace Squidex.Infrastructure.Orleans -{ - public sealed class OrleansPubSubGrain : Grain, IPubSubGrain - { - private readonly List subscriptions = new List(); - - public Task PublishAsync(object message) - { - foreach (var subscription in subscriptions) - { - try - { - subscription.Handle(message); - } - catch - { - continue; - } - } - - return Task.CompletedTask; - } - - public Task SubscribeAsync(IPubSubGrainObserver observer) - { - subscriptions.Add(observer); - - return Task.CompletedTask; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index 9ce729126..415fd3cd8 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -15,7 +15,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -37,6 +37,7 @@ + diff --git a/backend/src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs b/backend/src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs index 830403891..9713175bd 100644 --- a/backend/src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs +++ b/backend/src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs @@ -9,21 +9,22 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; -using Squidex.Infrastructure.Caching; namespace Squidex.Infrastructure.UsageTracking { - public sealed class CachingUsageTracker : CachingProviderBase, IUsageTracker + public sealed class CachingUsageTracker : IUsageTracker { private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(5); private readonly IUsageTracker inner; + private readonly IMemoryCache cache; public CachingUsageTracker(IUsageTracker inner, IMemoryCache cache) - : base(cache) { Guard.NotNull(inner, nameof(inner)); + Guard.NotNull(cache, nameof(cache)); this.inner = inner; + this.cache = cache; } public Task>> QueryAsync(string key, DateTime fromDate, DateTime toDate) @@ -46,7 +47,7 @@ namespace Squidex.Infrastructure.UsageTracking var cacheKey = string.Join("$", "Usage", nameof(GetForMonthAsync), key, date, category); - return Cache.GetOrCreateAsync(cacheKey, entry => + return cache.GetOrCreateAsync(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = CacheDuration; @@ -60,7 +61,7 @@ namespace Squidex.Infrastructure.UsageTracking var cacheKey = string.Join("$", "Usage", nameof(GetAsync), key, fromDate, toDate, category); - return Cache.GetOrCreateAsync(cacheKey, entry => + return cache.GetOrCreateAsync(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = CacheDuration; diff --git a/backend/src/Squidex.Web/Pipeline/LocalCacheMiddleware.cs b/backend/src/Squidex.Web/Pipeline/LocalCacheMiddleware.cs index b9f050191..3a69604aa 100644 --- a/backend/src/Squidex.Web/Pipeline/LocalCacheMiddleware.cs +++ b/backend/src/Squidex.Web/Pipeline/LocalCacheMiddleware.cs @@ -7,8 +7,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Squidex.Caching; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; namespace Squidex.Web.Pipeline { diff --git a/backend/src/Squidex/Config/Domain/InfrastructureServices.cs b/backend/src/Squidex/Config/Domain/InfrastructureServices.cs index d855656a8..17cab2d16 100644 --- a/backend/src/Squidex/Config/Domain/InfrastructureServices.cs +++ b/backend/src/Squidex/Config/Domain/InfrastructureServices.cs @@ -15,6 +15,7 @@ using Squidex.Areas.Api.Controllers.Contents.Generator; using Squidex.Areas.Api.Controllers.News; using Squidex.Areas.Api.Controllers.News.Service; using Squidex.Areas.Api.Controllers.UI; +using Squidex.Caching; using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Core.Scripting.Extensions; using Squidex.Domain.Apps.Core.Tags; @@ -24,7 +25,6 @@ using Squidex.Domain.Apps.Entities.Contents.Counter; using Squidex.Domain.Apps.Entities.Rules.UsageTracking; using Squidex.Domain.Apps.Entities.Tags; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.EventSourcing.Grains; using Squidex.Infrastructure.Orleans; @@ -49,6 +49,9 @@ namespace Squidex.Config.Domain services.Configure( config.GetSection("caching:replicated")); + services.AddReplicatedCache(); + services.AddAsyncLocalCache(); + services.AddSingletonAs(_ => SystemClock.Instance) .As(); @@ -58,15 +61,6 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .As(); - services.AddSingletonAs() - .As(); - - services.AddSingletonAs() - .As(); - - services.AddSingletonAs() - .As(); - services.AddSingletonAs() .AsOptional(); diff --git a/backend/src/Squidex/Config/Orleans/OrleansServices.cs b/backend/src/Squidex/Config/Orleans/OrleansServices.cs index 41ec6a8ce..ce836ed82 100644 --- a/backend/src/Squidex/Config/Orleans/OrleansServices.cs +++ b/backend/src/Squidex/Config/Orleans/OrleansServices.cs @@ -28,6 +28,8 @@ namespace Squidex.Config.Orleans { public static void ConfigureForSquidex(this ISiloBuilder builder, IConfiguration config) { + builder.AddOrleansPubSub(); + builder.ConfigureServices(siloServices => { siloServices.AddSingleton(); diff --git a/backend/src/Squidex/Squidex.csproj b/backend/src/Squidex/Squidex.csproj index 2e3edee69..2fb0153d2 100644 --- a/backend/src/Squidex/Squidex.csproj +++ b/backend/src/Squidex/Squidex.csproj @@ -58,7 +58,8 @@ - + + diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs index 38fb6b7aa..1c7335a9f 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Indexes/AppsIndexTests.cs @@ -10,12 +10,13 @@ using System.Collections.Generic; using System.Threading.Tasks; using FakeItEasy; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Orleans; +using Squidex.Caching; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Security; @@ -45,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes .Returns(indexByUser); var cache = - new ReplicatedCache(new MemoryCache(Options.Create(new MemoryCacheOptions())), new SimplePubSub(), + new ReplicatedCache(new MemoryCache(Options.Create(new MemoryCacheOptions())), new SimplePubSub(A.Fake>()), Options.Create(new ReplicatedCacheOptions { Enable = true })); sut = new AppsIndex(grainFactory, cache); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs index 85cf72960..ed7237d03 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs @@ -11,6 +11,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using FakeItEasy; +using Squidex.Caching; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; @@ -20,7 +21,6 @@ using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.EventSourcing; using Xunit; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs index cbff2b81a..8b672689d 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs @@ -12,13 +12,13 @@ using FakeItEasy; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; using NodaTime; +using Squidex.Caching; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.EventSourcing; using Xunit; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs index 832e0b513..a805dd7f1 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs @@ -10,12 +10,13 @@ using System.Collections.Generic; using System.Threading.Tasks; using FakeItEasy; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Orleans; +using Squidex.Caching; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Infrastructure; -using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Validation; @@ -40,7 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes .Returns(index); var cache = - new ReplicatedCache(new MemoryCache(Options.Create(new MemoryCacheOptions())), new SimplePubSub(), + new ReplicatedCache(new MemoryCache(Options.Create(new MemoryCacheOptions())), new SimplePubSub(A.Fake>()), Options.Create(new ReplicatedCacheOptions { Enable = true })); sut = new SchemasIndex(grainFactory, cache); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Caching/AsyncLocalCacheTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Caching/AsyncLocalCacheTests.cs deleted file mode 100644 index 4ac0b04a9..000000000 --- a/backend/tests/Squidex.Infrastructure.Tests/Caching/AsyncLocalCacheTests.cs +++ /dev/null @@ -1,123 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Threading.Tasks; -using Xunit; - -namespace Squidex.Infrastructure.Caching -{ - public class AsyncLocalCacheTests - { - private readonly ILocalCache sut = new AsyncLocalCache(); - private int called; - - [Fact] - public async Task Should_add_item_to_cache_when_context_exists() - { - using (sut.StartContext()) - { - sut.Add("Key", 1); - - await Task.Delay(5); - - AssertCache(sut, "Key", 1, true); - - await Task.Delay(5); - - sut.Remove("Key"); - - AssertCache(sut, "Key", null, false); - } - } - - [Fact] - public async Task Should_not_add_item_to_cache_when_context_not_exists() - { - sut.Add("Key", 1); - - await Task.Delay(5); - - AssertCache(sut, "Key", null, false); - - sut.Remove("Key"); - - await Task.Delay(5); - - AssertCache(sut, "Key", null, false); - } - - [Fact] - public async Task Should_call_creator_once_when_context_exists() - { - using (sut.StartContext()) - { - var value1 = await sut.GetOrCreateAsync("Key", () => Task.FromResult(++called)); - - await Task.Delay(5); - - var value2 = await sut.GetOrCreateAsync("Key", () => Task.FromResult(++called)); - - Assert.Equal(1, called); - Assert.Equal(1, value1); - Assert.Equal(1, value2); - } - } - - [Fact] - public async Task Should_call_creator_twice_when_context_not_exists() - { - var value1 = await sut.GetOrCreateAsync("Key", () => Task.FromResult(++called)); - - await Task.Delay(5); - - var value2 = await sut.GetOrCreateAsync("Key", () => Task.FromResult(++called)); - - Assert.Equal(2, called); - Assert.Equal(1, value1); - Assert.Equal(2, value2); - } - - [Fact] - public async Task Should_call_async_creator_once_when_context_exists() - { - using (sut.StartContext()) - { - var value1 = await sut.GetOrCreateAsync("Key", () => Task.FromResult(++called)); - - await Task.Delay(5); - - var value2 = await sut.GetOrCreateAsync("Key", () => Task.FromResult(++called)); - - Assert.Equal(1, called); - Assert.Equal(1, value1); - Assert.Equal(1, value2); - } - } - - [Fact] - public async Task Should_call_async_creator_twice_when_context_not_exists() - { - var value1 = await sut.GetOrCreateAsync("Key", () => Task.FromResult(++called)); - - await Task.Delay(5); - - var value2 = await sut.GetOrCreateAsync("Key", () => Task.FromResult(++called)); - - Assert.Equal(2, called); - Assert.Equal(1, value1); - Assert.Equal(2, value2); - } - - private static void AssertCache(ILocalCache cache, string key, object? expectedValue, bool expectedFound) - { - var found = cache.TryGetValue(key, out var value); - - Assert.Equal(expectedFound, found); - Assert.Equal(expectedValue, value); - } - } -} diff --git a/backend/tests/Squidex.Infrastructure.Tests/Caching/LRUCacheTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Caching/LRUCacheTests.cs deleted file mode 100644 index 8f765f076..000000000 --- a/backend/tests/Squidex.Infrastructure.Tests/Caching/LRUCacheTests.cs +++ /dev/null @@ -1,98 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using Xunit; - -namespace Squidex.Infrastructure.Caching -{ - public class LRUCacheTests - { - private readonly LRUCache sut = new LRUCache(10); - private readonly string key = "Key"; - - [Fact] - public void Should_always_override_when_setting_value() - { - sut.Set(key, 1); - sut.Set(key, 2); - - Assert.True(sut.TryGetValue(key, out var value)); - Assert.True(sut.Contains(key)); - - Assert.Equal(2, value); - } - - [Fact] - public void Should_clear_items() - { - sut.Set("1", 1); - sut.Set("2", 2); - - Assert.Equal(2, sut.Count); - - sut.Clear(); - - Assert.Equal(0, sut.Count); - } - - [Fact] - public void Should_remove_old_items_whentC_capacity_reached() - { - for (var i = 0; i < 15; i++) - { - sut.Set(i.ToString(), i); - } - - for (var i = 0; i < 5; i++) - { - Assert.False(sut.TryGetValue(i.ToString(), out var value)); - Assert.Equal(0, value); - } - - for (var i = 5; i < 15; i++) - { - Assert.True(sut.TryGetValue(i.ToString(), out var value)); - Assert.Equal(i, value); - } - } - - [Fact] - public void Should_notify_about_evicted_items() - { - var evicted = new List(); - - var cache = new LRUCache(3, (key, _) => evicted.Add(key)); - - cache.Set(1, 1); - cache.Set(2, 2); - cache.Set(3, 3); - cache.Set(1, 1); - cache.Set(4, 4); - cache.Set(5, 5); - - Assert.Equal(new List { 2, 3 }, evicted); - } - - [Fact] - public void Should_return_false_when_item_to_remove_does_not_exist() - { - Assert.False(sut.Remove(key)); - } - - [Fact] - public void Should_remove_inserted_item() - { - sut.Set(key, 2); - - Assert.True(sut.Remove(key)); - Assert.False(sut.Contains(key)); - Assert.False(sut.TryGetValue(key, out var value)); - Assert.Equal(0, value); - } - } -} \ No newline at end of file diff --git a/backend/tests/Squidex.Infrastructure.Tests/Caching/ReplicatedCacheTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Caching/ReplicatedCacheTests.cs deleted file mode 100644 index 58a60a841..000000000 --- a/backend/tests/Squidex.Infrastructure.Tests/Caching/ReplicatedCacheTests.cs +++ /dev/null @@ -1,141 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Threading.Tasks; -using FakeItEasy; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Options; -using Xunit; - -namespace Squidex.Infrastructure.Caching -{ - public class ReplicatedCacheTests - { - private readonly IPubSub pubSub = A.Fake(options => options.CallsBaseMethods()); - private readonly ReplicatedCacheOptions options = new ReplicatedCacheOptions { Enable = true }; - private readonly ReplicatedCache sut; - - public ReplicatedCacheTests() - { - sut = new ReplicatedCache(CreateMemoryCache(), pubSub, Options.Create(options)); - } - - [Fact] - public void Should_serve_from_cache() - { - sut.Add("Key", 1, TimeSpan.FromMinutes(10), true); - - AssertCache(sut, "Key", 1, true); - - sut.Remove("Key"); - - AssertCache(sut, "Key", null, false); - } - - [Fact] - public void Should_not_serve_from_cache_disabled() - { - options.Enable = false; - - sut.Add("Key", 1, TimeSpan.FromMilliseconds(100), true); - - AssertCache(sut, "Key", null, false); - } - - [Fact] - public async Task Should_not_serve_from_cache_when_expired() - { - sut.Add("Key", 1, TimeSpan.FromMilliseconds(1), true); - - await Task.Delay(100); - - AssertCache(sut, "Key", null, false); - } - - [Fact] - public void Should_not_invalidate_other_instances_when_item_added_and_flag_is_false() - { - var cache1 = new ReplicatedCache(CreateMemoryCache(), pubSub, Options.Create(options)); - var cache2 = new ReplicatedCache(CreateMemoryCache(), pubSub, Options.Create(options)); - - cache1.Add("Key", 1, TimeSpan.FromMinutes(1), false); - cache2.Add("Key", 2, TimeSpan.FromMinutes(1), false); - - AssertCache(cache1, "Key", 1, true); - AssertCache(cache2, "Key", 2, true); - } - - [Fact] - public void Should_invalidate_other_instances_when_item_added_and_flag_is_true() - { - var cache1 = new ReplicatedCache(CreateMemoryCache(), pubSub, Options.Create(options)); - var cache2 = new ReplicatedCache(CreateMemoryCache(), pubSub, Options.Create(options)); - - cache1.Add("Key", 1, TimeSpan.FromMinutes(1), true); - cache2.Add("Key", 2, TimeSpan.FromMinutes(1), true); - - AssertCache(cache1, "Key", null, false); - AssertCache(cache2, "Key", 2, true); - } - - [Fact] - public void Should_invalidate_other_instances_when_item_removed() - { - var cache1 = new ReplicatedCache(CreateMemoryCache(), pubSub, Options.Create(options)); - var cache2 = new ReplicatedCache(CreateMemoryCache(), pubSub, Options.Create(options)); - - cache1.Add("Key", 1, TimeSpan.FromMinutes(1), true); - cache2.Remove("Key"); - - AssertCache(cache1, "Key", null, false); - AssertCache(cache2, "Key", null, false); - } - - [Fact] - public void Should_send_invalidation_message_when_added_and_flag_is_true() - { - sut.Add("Key", 1, TimeSpan.FromMinutes(1), true); - - A.CallTo(() => pubSub.Publish(A._)) - .MustHaveHappened(); - } - - [Fact] - public void Should_not_send_invalidation_message_when_added_flag_is_false() - { - sut.Add("Key", 1, TimeSpan.FromMinutes(1), false); - - A.CallTo(() => pubSub.Publish(A._)) - .MustNotHaveHappened(); - } - - [Fact] - public void Should_not_send_invalidation_message_when_added_but_disabled() - { - options.Enable = false; - - sut.Add("Key", 1, TimeSpan.FromMinutes(1), true); - - A.CallTo(() => pubSub.Publish(A._)) - .MustNotHaveHappened(); - } - - private static void AssertCache(IReplicatedCache cache, string key, object? expectedValue, bool expectedFound) - { - var found = cache.TryGetValue(key, out var value); - - Assert.Equal(expectedFound, found); - Assert.Equal(expectedValue, value); - } - - private static MemoryCache CreateMemoryCache() - { - return new MemoryCache(Options.Create(new MemoryCacheOptions())); - } - } -} diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/PubSubTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/PubSubTests.cs deleted file mode 100644 index dd40da06e..000000000 --- a/backend/tests/Squidex.Infrastructure.Tests/Orleans/PubSubTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Orleans; -using Orleans.TestingHost; -using Xunit; - -namespace Squidex.Infrastructure.Orleans -{ - [Trait("Category", "Dependencies")] - public class PubSubTests - { - [Fact] - public async Task Simple_pubsub_tests() - { - var cluster = - new TestClusterBuilder(3) - .Build(); - - await cluster.DeployAsync(); - - var sent = new HashSet - { - Guid.NewGuid(), - Guid.NewGuid(), - Guid.NewGuid() - }; - - var received1 = await CreateSubscriber(cluster.Client, sent.Count); - var received2 = await CreateSubscriber(cluster.Client, sent.Count); - - var pubSub = new OrleansPubSub(cluster.Client); - - foreach (var message in sent) - { - pubSub.Publish(message); - } - - await Task.WhenAny( - Task.WhenAll( - received1, - received2 - ), - Task.Delay(10000)); - - Assert.True(received1.Result.SetEquals(sent)); - Assert.True(received2.Result.SetEquals(sent)); - } - - private static async Task>> CreateSubscriber(IGrainFactory grainFactory, int expectedCount) - { - var pubSub = new OrleansPubSub(grainFactory); - - await pubSub.StartAsync(default); - - var received = new HashSet(); - var receivedCompleted = new TaskCompletionSource>(); - - pubSub.Subscribe(message => - { - if (message is Guid guid) - { - received.Add(guid); - } - - if (received.Count == expectedCount) - { - receivedCompleted.TrySetResult(received); - } - }); - - return receivedCompleted.Task; - } - } -}