Browse Source

Better caching for plan usage.

pull/942/head
Sebastian 4 years ago
parent
commit
e13fa397b2
  1. 4
      backend/src/Squidex.Domain.Apps.Entities/Billing/IUsageGate.cs
  2. 27
      backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.cs
  3. 12
      backend/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs
  4. 2
      backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs
  5. 2
      backend/src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs
  6. 4
      backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs
  7. 10
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageGateTests.cs

4
backend/src/Squidex.Domain.Apps.Entities/Billing/IUsageGate.cs

@ -28,10 +28,10 @@ public interface IUsageGate
Task DeleteAssetsUsageAsync(
CancellationToken ct = default);
Task<(Plan Plan, string PlanId, DomainId? TeamId)> GetPlanForAppAsync(IAppEntity app,
Task<(Plan Plan, string PlanId, DomainId? TeamId)> GetPlanForAppAsync(IAppEntity app, bool canCache,
CancellationToken ct = default);
Task<(Plan Plan, string PlanId, DomainId? TeamId)> GetPlanForAppAsync(DomainId appId,
Task<(Plan Plan, string PlanId, DomainId? TeamId)> GetPlanForAppAsync(DomainId appId, bool canCache,
CancellationToken ct = default);
Task<(Plan Plan, string PlanId)> GetPlanForTeamAsync(ITeamEntity team,

27
backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.cs

@ -134,7 +134,7 @@ public sealed class UsageGate : IUsageGate, IAssetUsageTracker
Guard.NotNull(app);
// Resolve the plan from either the app or the assigned team.
var (plan, _, teamId) = await GetPlanForAppAsync(app, ct);
var (plan, _, teamId) = await GetPlanForAppAsync(app, true, ct);
var appId = app.Id;
var blocking = false;
@ -222,7 +222,7 @@ public sealed class UsageGate : IUsageGate, IAssetUsageTracker
usageTracker.TrackAsync(SummaryDate, appKey, null, counters, ct)
};
var (_, _, teamId) = await GetPlanForAppAsync(appId, ct);
var (_, _, teamId) = await GetPlanForAppAsync(appId, true, ct);
if (teamId != null)
{
@ -235,12 +235,17 @@ public sealed class UsageGate : IUsageGate, IAssetUsageTracker
await Task.WhenAll(tasks);
}
public Task<(Plan Plan, string PlanId, DomainId? TeamId)> GetPlanForAppAsync(IAppEntity app,
public Task<(Plan Plan, string PlanId, DomainId? TeamId)> GetPlanForAppAsync(IAppEntity app, bool canCache,
CancellationToken ct = default)
{
Guard.NotNull(app);
return memoryCache.GetOrCreateAsync(app, async x =>
if (!canCache)
{
return GetPlanCoreAsync(app, ct);
}
return memoryCache.GetOrCreateAsync(CacheKey(app.Id), async x =>
{
x.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
@ -248,10 +253,15 @@ public sealed class UsageGate : IUsageGate, IAssetUsageTracker
});
}
public Task<(Plan Plan, string PlanId, DomainId? TeamId)> GetPlanForAppAsync(DomainId appId,
public Task<(Plan Plan, string PlanId, DomainId? TeamId)> GetPlanForAppAsync(DomainId appId, bool canCache,
CancellationToken ct = default)
{
return memoryCache.GetOrCreateAsync(appId, async x =>
if (!canCache)
{
return GetPlanCoreAsync(appId, ct);
}
return memoryCache.GetOrCreateAsync(CacheKey(appId), async x =>
{
x.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
@ -310,4 +320,9 @@ public sealed class UsageGate : IUsageGate, IAssetUsageTracker
{
return $"{appId}_TeamAssets";
}
private static string CacheKey(DomainId appId)
{
return $"{appId}_Plan";
}
}

12
backend/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs

@ -6,7 +6,6 @@
// ==========================================================================
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Billing;
@ -48,14 +47,9 @@ public sealed class AppContributorsController : ApiController
[ProducesResponseType(typeof(ContributorsDto), StatusCodes.Status200OK)]
[ApiPermissionOrAnonymous(PermissionIds.AppContributorsRead)]
[ApiCosts(0)]
public IActionResult GetContributors(string app)
public async Task<IActionResult> GetContributors(string app)
{
var response = Deferred.AsyncResponse(() =>
{
return GetResponseAsync(App, false);
});
Response.Headers[HeaderNames.ETag] = App.ToEtag();
var response = await GetResponseAsync(App, false);
return Ok(response);
}
@ -145,7 +139,7 @@ public sealed class AppContributorsController : ApiController
private async Task<ContributorsDto> GetResponseAsync(IAppEntity app, bool invited)
{
var (plan, _, _) = await usageGate.GetPlanForAppAsync(app, HttpContext.RequestAborted);
var (plan, _, _) = await usageGate.GetPlanForAppAsync(app, false, HttpContext.RequestAborted);
return await ContributorsDto.FromDomainAsync(app, Resources, userResolver, plan, invited);
}

2
backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs

@ -469,7 +469,7 @@ public sealed class AssetsController : ApiController
throw new ValidationException(error);
}
var (plan, _, _) = await usageGate.GetPlanForAppAsync(App, HttpContext.RequestAborted);
var (plan, _, _) = await usageGate.GetPlanForAppAsync(App, true, HttpContext.RequestAborted);
var currentSize = await assetUsageTracker.GetTotalSizeByAppAsync(AppId, HttpContext.RequestAborted);

2
backend/src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs

@ -60,7 +60,7 @@ public sealed class AppPlansController : ApiController
var (plan, link, referral) =
await AsyncHelper.WhenAll(
usageGate.GetPlanForAppAsync(App, HttpContext.RequestAborted),
usageGate.GetPlanForAppAsync(App, false, HttpContext.RequestAborted),
billingManager.GetPortalLinkAsync(UserId, App, HttpContext.RequestAborted),
billingManager.GetReferralInfoAsync(UserId, App, HttpContext.RequestAborted));

4
backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs

@ -126,7 +126,7 @@ public sealed class UsagesController : ApiController
var (summary, details) = await usageTracker.QueryAsync(AppId.ToString(), fromDate.Date, toDate.Date, HttpContext.RequestAborted);
// Use the current app plan to show the limits to the user.
var (plan, _, _) = await usageGate.GetPlanForAppAsync(App, HttpContext.RequestAborted);
var (plan, _, _) = await usageGate.GetPlanForAppAsync(App, false, HttpContext.RequestAborted);
var response = CallsUsageDtoDto.FromDomain(plan, summary, details);
@ -185,7 +185,7 @@ public sealed class UsagesController : ApiController
var size = await assetStatsRepository.GetTotalSizeByAppAsync(AppId, HttpContext.RequestAborted);
// Use the current app plan to show the limits to the user.
var (plan, _, _) = await usageGate.GetPlanForAppAsync(App, HttpContext.RequestAborted);
var (plan, _, _) = await usageGate.GetPlanForAppAsync(App, false, HttpContext.RequestAborted);
var response = new CurrentStorageDto { Size = size, MaxAllowed = plan.MaxAssetSize };

10
backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageGateTests.cs

@ -82,7 +82,7 @@ public class UsageGateTests
[Fact]
public async Task Should_get_free_plan_for_app()
{
var plan = await sut.GetPlanForAppAsync(appWithoutTeam, ct);
var plan = await sut.GetPlanForAppAsync(appWithoutTeam, false, ct);
Assert.Equal((planFree, planFree.Id, null), plan);
}
@ -98,7 +98,7 @@ public class UsageGateTests
A.CallTo(() => team.Id)
.Returns(teamId);
var plan = await sut.GetPlanForAppAsync(appWithTeam, ct);
var plan = await sut.GetPlanForAppAsync(appWithTeam, false, ct);
Assert.Equal((planFree, planFree.Id, teamId), plan);
}
@ -109,7 +109,7 @@ public class UsageGateTests
A.CallTo(() => appWithoutTeam.Plan)
.Returns(new AssignedPlan(RefToken.User("1"), planPaid.Id));
var plan = await sut.GetPlanForAppAsync(appWithoutTeam, ct);
var plan = await sut.GetPlanForAppAsync(appWithoutTeam, false, ct);
Assert.Equal((planPaid, planPaid.Id, null), plan);
}
@ -123,7 +123,7 @@ public class UsageGateTests
A.CallTo(() => appWithoutTeam.Plan)
.Returns(new AssignedPlan(RefToken.User("1"), planPaid.Id));
var plan = await sut.GetPlanForAppAsync(appWithoutTeam.Id, ct);
var plan = await sut.GetPlanForAppAsync(appWithoutTeam.Id, false, ct);
Assert.Equal((planPaid, planPaid.Id, null), plan);
}
@ -142,7 +142,7 @@ public class UsageGateTests
A.CallTo(() => team.Plan)
.Returns(new AssignedPlan(RefToken.User("1"), planPaid.Id));
var plan = await sut.GetPlanForAppAsync(appWithTeam, ct);
var plan = await sut.GetPlanForAppAsync(appWithTeam, false, ct);
Assert.Equal((planPaid, planPaid.Id, teamId), plan);
}

Loading…
Cancel
Save