From 8476e5bfc0bdb9bac33b0e31644fc5fa18bedb63 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 4 Jul 2019 21:39:52 +0200 Subject: [PATCH] Performance improvements: 1. Caching for app tags 2. Caching for available languages. --- .../Tags/ITagService.cs | 6 ++--- .../Tags/{TagSet.cs => TagsExport.cs} | 2 +- .../Tags/TagsSet.cs | 26 +++++++++++++++++++ .../Assets/BackupAssets.cs | 2 +- .../Tags/GrainTagService.cs | 6 ++--- .../Tags/ITagGrain.cs | 6 ++--- .../Tags/TagGrain.cs | 12 +++++---- .../Controllers/Assets/AssetsController.cs | 6 +++-- .../app/shared/state/languages.state.ts | 22 +++++++++++++--- .../Tags/GrainTagServiceTests.cs | 2 +- .../Tags/TagGrainTests.cs | 2 +- 11 files changed, 68 insertions(+), 24 deletions(-) rename src/Squidex.Domain.Apps.Core.Operations/Tags/{TagSet.cs => TagsExport.cs} (88%) create mode 100644 src/Squidex.Domain.Apps.Core.Operations/Tags/TagsSet.cs diff --git a/src/Squidex.Domain.Apps.Core.Operations/Tags/ITagService.cs b/src/Squidex.Domain.Apps.Core.Operations/Tags/ITagService.cs index f0fc88a3a..ad819ba57 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Tags/ITagService.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/Tags/ITagService.cs @@ -19,11 +19,11 @@ namespace Squidex.Domain.Apps.Core.Tags Task> DenormalizeTagsAsync(Guid appId, string group, HashSet ids); - Task> GetTagsAsync(Guid appId, string group); + Task GetTagsAsync(Guid appId, string group); - Task GetExportableTagsAsync(Guid appId, string group); + Task GetExportableTagsAsync(Guid appId, string group); - Task RebuildTagsAsync(Guid appId, string group, TagSet tags); + Task RebuildTagsAsync(Guid appId, string group, TagsExport tags); Task ClearAsync(Guid appId, string group); } diff --git a/src/Squidex.Domain.Apps.Core.Operations/Tags/TagSet.cs b/src/Squidex.Domain.Apps.Core.Operations/Tags/TagsExport.cs similarity index 88% rename from src/Squidex.Domain.Apps.Core.Operations/Tags/TagSet.cs rename to src/Squidex.Domain.Apps.Core.Operations/Tags/TagsExport.cs index 530c28b00..d1f54ecf7 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Tags/TagSet.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/Tags/TagsExport.cs @@ -9,7 +9,7 @@ using System.Collections.Generic; namespace Squidex.Domain.Apps.Core.Tags { - public sealed class TagSet : Dictionary + public sealed class TagsExport : Dictionary { } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/Tags/TagsSet.cs b/src/Squidex.Domain.Apps.Core.Operations/Tags/TagsSet.cs new file mode 100644 index 000000000..8e87ee8ab --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/Tags/TagsSet.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; + +namespace Squidex.Domain.Apps.Core.Tags +{ + public sealed class TagsSet : Dictionary + { + public long Version { get; set; } + + public TagsSet() + { + } + + public TagsSet(IDictionary tags, long version) + : base(tags) + { + Version = version; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs b/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs index 068a807de..44701ee16 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs @@ -82,7 +82,7 @@ namespace Squidex.Domain.Apps.Entities.Assets private async Task RestoreTagsAsync(Guid appId, BackupReader reader) { - var tags = await reader.ReadJsonAttachmentAsync(TagsFile); + var tags = await reader.ReadJsonAttachmentAsync(TagsFile); await tagService.RebuildTagsAsync(appId, TagGroups.Assets, tags); } diff --git a/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs b/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs index ad8c37457..08f1ff835 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs @@ -45,17 +45,17 @@ namespace Squidex.Domain.Apps.Entities.Tags return GetGrain(appId, group).DenormalizeTagsAsync(ids); } - public Task> GetTagsAsync(Guid appId, string group) + public Task GetTagsAsync(Guid appId, string group) { return GetGrain(appId, group).GetTagsAsync(); } - public Task GetExportableTagsAsync(Guid appId, string group) + public Task GetExportableTagsAsync(Guid appId, string group) { return GetGrain(appId, group).GetExportableTagsAsync(); } - public Task RebuildTagsAsync(Guid appId, string group, TagSet tags) + public Task RebuildTagsAsync(Guid appId, string group, TagsExport tags) { return GetGrain(appId, group).RebuildAsync(tags); } diff --git a/src/Squidex.Domain.Apps.Entities/Tags/ITagGrain.cs b/src/Squidex.Domain.Apps.Entities/Tags/ITagGrain.cs index d43b6f022..be9a5bdfb 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/ITagGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/ITagGrain.cs @@ -20,12 +20,12 @@ namespace Squidex.Domain.Apps.Entities.Tags Task> DenormalizeTagsAsync(HashSet ids); - Task> GetTagsAsync(); + Task GetTagsAsync(); - Task GetExportableTagsAsync(); + Task GetExportableTagsAsync(); Task ClearAsync(); - Task RebuildAsync(TagSet tags); + Task RebuildAsync(TagsExport tags); } } diff --git a/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs b/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs index 9062e3366..3053bf1a6 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Tags [CollectionName("Index_Tags")] public sealed class GrainState { - public TagSet Tags { get; set; } = new TagSet(); + public TagsExport Tags { get; set; } = new TagsExport(); } public TagGrain(IStore store) @@ -33,7 +33,7 @@ namespace Squidex.Domain.Apps.Entities.Tags return ClearStateAsync(); } - public Task RebuildAsync(TagSet tags) + public Task RebuildAsync(TagsExport tags) { State.Tags = tags; @@ -132,12 +132,14 @@ namespace Squidex.Domain.Apps.Entities.Tags return Task.FromResult(result); } - public Task> GetTagsAsync() + public Task GetTagsAsync() { - return Task.FromResult(State.Tags.Values.ToDictionary(x => x.Name, x => x.Count)); + var tags = State.Tags.Values.ToDictionary(x => x.Name, x => x.Count); + + return Task.FromResult(new TagsSet(tags, Persistence.Version)); } - public Task GetExportableTagsAsync() + public Task GetExportableTagsAsync() { return Task.FromResult(State.Tags); } diff --git a/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs b/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs index 84f423dda..0456767f0 100644 --- a/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs @@ -77,9 +77,11 @@ namespace Squidex.Areas.Api.Controllers.Assets [ApiCosts(1)] public async Task GetTags(string app) { - var response = await tagService.GetTagsAsync(AppId, TagGroups.Assets); + var tags = await tagService.GetTagsAsync(AppId, TagGroups.Assets); - return Ok(response); + Response.Headers[HeaderNames.ETag] = tags.Version.ToString(); + + return Ok(tags); } /// diff --git a/src/Squidex/app/shared/state/languages.state.ts b/src/Squidex/app/shared/state/languages.state.ts index 7f1a77f27..fa792951a 100644 --- a/src/Squidex/app/shared/state/languages.state.ts +++ b/src/Squidex/app/shared/state/languages.state.ts @@ -7,7 +7,7 @@ import { Injectable } from '@angular/core'; import { forkJoin, Observable } from 'rxjs'; -import { map, tap } from 'rxjs/operators'; +import { map, shareReplay, tap } from 'rxjs/operators'; import { DialogService, @@ -65,6 +65,8 @@ type LanguageResultList = ImmutableArray; @Injectable() export class LanguagesState extends State { + private cachedLanguage$: Observable; + public languages = this.project(x => x.languages); @@ -96,9 +98,7 @@ export class LanguagesState extends State { this.resetState(); } - return forkJoin( - this.languagesService.getLanguages(), - this.appLanguagesService.getLanguages(this.appName)).pipe( + return forkJoin(this.getAllLanguages(), this.getAppLanguages()).pipe( map(args => { return { allLanguages: args[0], languages: args[1] }; }), @@ -166,6 +166,20 @@ export class LanguagesState extends State { return this.snapshot.version; } + private getAppLanguages() { + return this.appLanguagesService.getLanguages(this.appName); + } + + private getAllLanguages() { + if (!this.cachedLanguage$) { + this.cachedLanguage$ = + this.languagesService.getLanguages().pipe( + shareReplay(1)); + } + + return this.cachedLanguage$; + } + private createLanguage(language: AppLanguageDto, languages: AppLanguagesList): SnapshotLanguage { return { language, diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/GrainTagServiceTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/GrainTagServiceTests.cs index 5f449249f..aac32efb0 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/GrainTagServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/GrainTagServiceTests.cs @@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Tags [Fact] public async Task Should_call_grain_when_rebuilding() { - var tags = new TagSet(); + var tags = new TagsExport(); await sut.RebuildTagsAsync(appId, TagGroups.Assets, tags); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs index 9642dd244..562ceb805 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagGrainTests.cs @@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Entities.Tags [Fact] public async Task Should_rebuild_tags() { - var tags = new TagSet + var tags = new TagsExport { ["id1"] = new Tag { Name = "name1", Count = 1 }, ["id2"] = new Tag { Name = "name2", Count = 2 },