diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Diagnostics/OrleansAppsHealthCheck.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Diagnostics/OrleansAppsHealthCheck.cs index 729ef1441..2ba4138f9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Diagnostics/OrleansAppsHealthCheck.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Diagnostics/OrleansAppsHealthCheck.cs @@ -17,20 +17,25 @@ namespace Squidex.Domain.Apps.Entities.Apps.Diagnostics { public sealed class OrleansAppsHealthCheck : IHealthCheck { - private readonly IAppsByNameIndexGrain index; + private readonly IGrainFactory grainFactory; public OrleansAppsHealthCheck(IGrainFactory grainFactory) { Guard.NotNull(grainFactory, nameof(grainFactory)); - index = grainFactory.GetGrain(SingleGrain.Id); + this.grainFactory = grainFactory; } public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { - await index.CountAsync(); + await GetGrain().CountAsync(); return HealthCheckResult.Healthy("Orleans must establish communication."); } + + private IAppsByNameIndexGrain GetGrain() + { + return grainFactory.GetGrain(SingleGrain.Id); + } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageGate.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageGate.cs index 25c9a56c0..a377c34e7 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageGate.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Plans/UsageGate.cs @@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans private readonly MemoryCache memoryCache = new MemoryCache(Options.Create(new MemoryCacheOptions())); private readonly IAppPlansProvider appPlansProvider; private readonly IApiUsageTracker apiUsageTracker; - private readonly IUsageNotifierGrain usageLimitNotifier; + private readonly IGrainFactory grainFactory; public UsageGate(IAppPlansProvider appPlansProvider, IApiUsageTracker apiUsageTracker, IGrainFactory grainFactory) { @@ -34,8 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans this.appPlansProvider = appPlansProvider; this.apiUsageTracker = apiUsageTracker; - - usageLimitNotifier = grainFactory.GetGrain(SingleGrain.Id); + this.grainFactory = grainFactory; } public virtual async Task IsBlockedAsync(IAppEntity app, string? clientId, DateTime today) @@ -72,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans Users = users }; - usageLimitNotifier.NotifyAsync(notification).Forget(); + GetGrain().NotifyAsync(notification).Forget(); TrackNotified(appId); } @@ -83,6 +82,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Plans return isBlocked; } + private IUsageNotifierGrain GetGrain() + { + return grainFactory.GetGrain(SingleGrain.Id); + } + private bool HasNotifiedBefore(Guid appId) { return memoryCache.Get(appId); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Comments/CommentsCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Comments/CommentsCommandMiddleware.cs index 860159c27..b1dd45d06 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Comments/CommentsCommandMiddleware.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Comments/CommentsCommandMiddleware.cs @@ -52,13 +52,16 @@ namespace Squidex.Domain.Apps.Entities.Comments private async Task ExecuteCommandAsync(CommandContext context, CommentsCommand commentsCommand) { - var grain = grainFactory.GetGrain(commentsCommand.CommentsId); - - var result = await grain.ExecuteAsync(commentsCommand.AsJ()); + var result = await GetGrain(commentsCommand).ExecuteAsync(commentsCommand.AsJ()); context.Complete(result.Value); } + private ICommentsGrain GetGrain(CommentsCommand commentsCommand) + { + return grainFactory.GetGrain(commentsCommand.CommentsId); + } + private static bool IsMention(CreateComment createComment) { return createComment.IsMention; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerCommandMiddleware.cs index 8431cc114..24d75a853 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerCommandMiddleware.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerCommandMiddleware.cs @@ -17,13 +17,13 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking { public sealed class UsageTrackerCommandMiddleware : ICommandMiddleware { - private readonly IUsageTrackerGrain usageTrackerGrain; + private readonly IGrainFactory grainFactory; public UsageTrackerCommandMiddleware(IGrainFactory grainFactory) { Guard.NotNull(grainFactory, nameof(grainFactory)); - usageTrackerGrain = grainFactory.GetGrain(SingleGrain.Id); + this.grainFactory = grainFactory; } public async Task HandleAsync(CommandContext context, NextDelegate next) @@ -31,13 +31,13 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking switch (context.Command) { case DeleteRule deleteRule: - await usageTrackerGrain.RemoveTargetAsync(deleteRule.RuleId); + await GetGrain().RemoveTargetAsync(deleteRule.RuleId); break; case CreateRule createRule: { if (createRule.Trigger is UsageTrigger usage) { - await usageTrackerGrain.AddTargetAsync(createRule.RuleId, createRule.AppId, usage.Limit, usage.NumDays); + await GetGrain().AddTargetAsync(createRule.RuleId, createRule.AppId, usage.Limit, usage.NumDays); } break; @@ -47,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking { if (ruleUpdated.Trigger is UsageTrigger usage) { - await usageTrackerGrain.UpdateTargetAsync(ruleUpdated.RuleId, usage.Limit, usage.NumDays); + await GetGrain().UpdateTargetAsync(ruleUpdated.RuleId, usage.Limit, usage.NumDays); } break; @@ -56,5 +56,10 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking await next(context); } + + private IUsageTrackerGrain GetGrain() + { + return grainFactory.GetGrain(SingleGrain.Id); + } } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/Orleans/AsyncLocalTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Orleans/AsyncLocalTests.cs new file mode 100644 index 000000000..d320d32ae --- /dev/null +++ b/backend/tests/Squidex.Infrastructure.Tests/Orleans/AsyncLocalTests.cs @@ -0,0 +1,54 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading; +using System.Threading.Tasks; +using Orleans; +using Orleans.TestingHost; +using Xunit; + +namespace Squidex.Infrastructure.Orleans +{ + [Trait("Category", "Dependencies")] + public class AsyncLocalTests + { + public interface IAsyncLocalGrain : IGrainWithStringKey + { + public Task GetValueAsync(); + } + + public class AsyncLocalGrain : Grain, IAsyncLocalGrain + { + private readonly AsyncLocal temp = new AsyncLocal(); + + public Task GetValueAsync() + { + temp.Value++; + + return Task.FromResult(temp.Value); + } + } + + [Fact] + public async Task Should_use_async_local() + { + var cluster = + new TestClusterBuilder(1) + .Build(); + + await cluster.DeployAsync(); + + var grain = cluster.GrainFactory.GetGrain(SingleGrain.Id); + + var result1 = await grain.GetValueAsync(); + var result2 = await grain.GetValueAsync(); + + Assert.Equal(1, result1); + Assert.Equal(1, result2); + } + } +}