From 669e0e9ca5557459f14aa42eba10b6e96a1e6d9d Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 20 Apr 2020 13:52:22 +0300 Subject: [PATCH] Docs module cache clear resolves https://github.com/abpframework/abp/issues/672 --- .../Docs/Admin/Documents/ClearCacheInput.cs | 9 ++++ .../Documents/IDocumentAdminAppService.cs | 2 + .../Documents/DocumentAdminAppService.cs | 54 +++++++++++++++++++ .../Docs/Admin/DocumentsAdminController.cs | 7 +++ .../Pages/Docs/Admin/Projects/index.js | 12 +++++ .../Volo/Docs/Documents/DocumentAppService.cs | 46 ++++++++-------- .../Volo/Docs/Projects/ProjectAppService.cs | 5 +- .../Volo/Docs/Caching/CacheKeyGenerator.cs | 27 ++++++++++ .../Volo/Docs/Localization/Domain/en.json | 3 ++ 9 files changed, 139 insertions(+), 26 deletions(-) create mode 100644 modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/ClearCacheInput.cs create mode 100644 modules/docs/src/Volo.Docs.Domain/Volo/Docs/Caching/CacheKeyGenerator.cs diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/ClearCacheInput.cs b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/ClearCacheInput.cs new file mode 100644 index 0000000000..6497701c56 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/ClearCacheInput.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Docs.Admin.Documents +{ + public class ClearCacheInput + { + public Guid ProjectId { get; set; } + } +} diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/IDocumentAdminAppService.cs b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/IDocumentAdminAppService.cs index 1f18c0e64b..475b94acb5 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/IDocumentAdminAppService.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Documents/IDocumentAdminAppService.cs @@ -5,6 +5,8 @@ namespace Volo.Docs.Admin.Documents { public interface IDocumentAdminAppService : IApplicationService { + Task ClearCacheAsync(ClearCacheInput input); + Task PullAllAsync(PullAllDocumentInput input); Task PullAsync(PullDocumentInput input); diff --git a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs index 1000091b65..7e8e5dffd7 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs @@ -7,8 +7,10 @@ using Newtonsoft.Json; using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.Caching; +using Volo.Docs.Caching; using Volo.Docs.Documents; using Volo.Docs.Documents.FullSearch.Elastic; +using Volo.Docs.Localization; using Volo.Docs.Projects; using Volo.Extensions; @@ -21,19 +23,63 @@ namespace Volo.Docs.Admin.Documents private readonly IDocumentRepository _documentRepository; private readonly IDocumentSourceFactory _documentStoreFactory; private readonly IDistributedCache _documentUpdateCache; + private readonly IDistributedCache> _versionCache; + private readonly IDistributedCache _languageCache; + private readonly IDistributedCache _resourceCache; private readonly IDocumentFullSearch _documentFullSearch; public DocumentAdminAppService(IProjectRepository projectRepository, IDocumentRepository documentRepository, IDocumentSourceFactory documentStoreFactory, IDistributedCache documentUpdateCache, + IDistributedCache> versionCache, + IDistributedCache languageCache, + IDistributedCache resourceCache, IDocumentFullSearch documentFullSearch) { _projectRepository = projectRepository; _documentRepository = documentRepository; _documentStoreFactory = documentStoreFactory; _documentUpdateCache = documentUpdateCache; + _versionCache = versionCache; + _languageCache = languageCache; + _resourceCache = resourceCache; _documentFullSearch = documentFullSearch; + + LocalizationResource = typeof(DocsResource); + } + + public async Task ClearCacheAsync(ClearCacheInput input) + { + var project = await _projectRepository.GetAsync(input.ProjectId); + + var languageCacheKey = CacheKeyGenerator.GenerateProjectLanguageCacheKey(project); + var languageConfig = await _languageCache.GetAsync(languageCacheKey, true); + CheckNotNullCacheValue(languageConfig); + + var versionCacheKey = CacheKeyGenerator.GenerateProjectVersionsCacheKey(project); + var versions = await _versionCache.GetAsync(versionCacheKey, true); + CheckNotNullCacheValue(versions); + + await _languageCache.RemoveAsync(languageCacheKey, true); + await _versionCache.RemoveAsync(versionCacheKey, true); + + var documents = await _documentRepository.GetListAsync(); + + foreach (var languageCode in languageConfig.Languages) + { + foreach (var version in versions) + { + foreach (var document in documents) + { + var documentUpdateInfoCacheKey = CacheKeyGenerator.GenerateDocumentUpdateInfoCacheKey(project, document.Name, languageCode.Code, version.Name); + await _documentUpdateCache.RemoveAsync(documentUpdateInfoCacheKey); + + document.LastCachedTime = DateTime.MinValue; + await _documentRepository.UpdateAsync(document); + } + } + } } public async Task PullAllAsync(PullAllDocumentInput input) @@ -116,6 +162,14 @@ namespace Volo.Docs.Admin.Documents } } + private void CheckNotNullCacheValue(object cacheValue) + { + if (cacheValue == null) + { + throw new UserFriendlyException(L["CacheNullExceptionMessage"]); + } + } + private async Task UpdateDocumentUpdateInfoCache(Document document) { var cacheKey = $"DocumentUpdateInfo{document.ProjectId}#{document.Name}#{document.LanguageCode}#{document.Version}"; diff --git a/modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs b/modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs index 1dd1327266..3a6136d662 100644 --- a/modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs +++ b/modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/DocumentsAdminController.cs @@ -19,6 +19,13 @@ namespace Volo.Docs.Admin _documentAdminAppService = documentAdminAppService; } + [HttpPost] + [Route("ClearCache")] + public Task ClearCacheAsync(ClearCacheInput input) + { + return _documentAdminAppService.ClearCacheAsync(input); + } + [HttpPost] [Route("PullAll")] public Task PullAllAsync(PullAllDocumentInput input) diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js index b843202dd9..de7faea5e2 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js +++ b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js @@ -62,6 +62,18 @@ Id: data.record.id }); } + }, + { + text: l('ClearCache'), + visible: abp.auth.isGranted('Docs.Admin.Documents'), + confirmMessage: function (data) { return l('ClearCacheConfirmationMessage', data.record.name); }, + action: function (data) { + volo.docs.admin.documentsAdmin + .clearCache({ projectId: data.record.id}) + .then(function () { + _dataTable.ajax.reload(); + }); + } } ] } diff --git a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs index 2bb618055e..02a7c171bd 100644 --- a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs +++ b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs @@ -11,6 +11,7 @@ using Nest; using Newtonsoft.Json; using Volo.Abp; using Volo.Abp.Caching; +using Volo.Docs.Caching; using Volo.Docs.Documents.FullSearch.Elastic; using Volo.Docs.Projects; using Volo.Extensions; @@ -22,8 +23,7 @@ namespace Volo.Docs.Documents private readonly IProjectRepository _projectRepository; private readonly IDocumentRepository _documentRepository; private readonly IDocumentSourceFactory _documentStoreFactory; - protected IDistributedCache LanguageCache { get; } - protected IDistributedCache ResourceCache { get; } + protected IDistributedCache ResourceCache { get; } protected IDistributedCache DocumentUpdateCache { get; } protected IHostEnvironment HostEnvironment { get; } private readonly IDocumentFullSearch _documentFullSearch; @@ -37,8 +37,7 @@ namespace Volo.Docs.Documents IProjectRepository projectRepository, IDocumentRepository documentRepository, IDocumentSourceFactory documentStoreFactory, - IDistributedCache languageCache, - IDistributedCache resourceCache, + IDistributedCache resourceCache, IDistributedCache documentUpdateCache, IHostEnvironment hostEnvironment, IDocumentFullSearch documentFullSearch, @@ -48,7 +47,6 @@ namespace Volo.Docs.Documents _projectRepository = projectRepository; _documentRepository = documentRepository; _documentStoreFactory = documentStoreFactory; - LanguageCache = languageCache; ResourceCache = resourceCache; DocumentUpdateCache = documentUpdateCache; HostEnvironment = hostEnvironment; @@ -70,7 +68,7 @@ namespace Volo.Docs.Documents input.LanguageCode, input.Version ); - } + } public virtual async Task GetDefaultAsync(GetDefaultDocumentInput input) { @@ -106,7 +104,7 @@ namespace Volo.Docs.Documents foreach (var leaf in leafs) { - var cacheKey = $"DocumentUpdateInfo{project.Id}#{leaf.Path}#{input.LanguageCode}#{input.Version}"; + var cacheKey = CacheKeyGenerator.GenerateDocumentUpdateInfoCacheKey(project, leaf.Path, input.LanguageCode, input.Version); var documentUpdateInfo = await DocumentUpdateCache.GetAsync(cacheKey); if (documentUpdateInfo != null) { @@ -122,31 +120,31 @@ namespace Volo.Docs.Documents public async Task GetResourceAsync(GetDocumentResourceInput input) { var project = await _projectRepository.GetAsync(input.ProjectId); - var cacheKey = $"Resource@{project.ShortName}#{input.LanguageCode}#{input.Name}#{input.Version}"; + var cacheKey = CacheKeyGenerator.GenerateDocumentResourceCacheKey(project, input.Name, input.LanguageCode, input.Version); input.Version = string.IsNullOrWhiteSpace(input.Version) ? project.LatestVersionBranchName : input.Version; - async Task GetResourceAsync() + async Task GetResourceAsync() { var source = _documentStoreFactory.Create(project.DocumentStoreType); - var documentResource = await source.GetResource(project, input.Name, input.LanguageCode, input.Version); - - return ObjectMapper.Map(documentResource); + return await source.GetResource(project, input.Name, input.LanguageCode, input.Version); } if (HostEnvironment.IsDevelopment()) { - return await GetResourceAsync(); + return ObjectMapper.Map(await GetResourceAsync()); } - return await ResourceCache.GetOrAddAsync( - cacheKey, - GetResourceAsync, - () => new DistributedCacheEntryOptions - { - AbsoluteExpirationRelativeToNow = _documentResourceAbsoluteExpiration, - SlidingExpiration = _documentResourceSlidingExpiration - } - ); + return ObjectMapper.Map( + await ResourceCache.GetOrAddAsync( + cacheKey, + GetResourceAsync, + () => new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = _documentResourceAbsoluteExpiration, + SlidingExpiration = _documentResourceSlidingExpiration + } + ) + ); } public async Task> SearchAsync(DocumentSearchInput input) @@ -229,7 +227,7 @@ namespace Volo.Docs.Documents return await GetDocumentAsync(documentName, project, languageCode, version, document); } - var cacheKey = $"DocumentUpdateInfo{document.ProjectId}#{document.Name}#{document.LanguageCode}#{document.Version}"; + var cacheKey = CacheKeyGenerator.GenerateDocumentUpdateInfoCacheKey(project, document.Name, document.LanguageCode, document.Version); await DocumentUpdateCache.SetAsync(cacheKey, new DocumentUpdateInfo { Name = document.Name, @@ -261,7 +259,7 @@ namespace Volo.Docs.Documents Logger.LogInformation($"Document retrieved: {documentName}"); - var cacheKey = $"DocumentUpdateInfo{sourceDocument.ProjectId}#{sourceDocument.Name}#{sourceDocument.LanguageCode}#{sourceDocument.Version}"; + var cacheKey = CacheKeyGenerator.GenerateDocumentUpdateInfoCacheKey(project, sourceDocument.Name, sourceDocument.LanguageCode, sourceDocument.Version); await DocumentUpdateCache.SetAsync(cacheKey, new DocumentUpdateInfo { Name = sourceDocument.Name, diff --git a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Projects/ProjectAppService.cs b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Projects/ProjectAppService.cs index c05857f588..04b96f9e6f 100644 --- a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Projects/ProjectAppService.cs +++ b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Projects/ProjectAppService.cs @@ -7,6 +7,7 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.Caching; using Volo.Abp.Guids; +using Volo.Docs.Caching; using Volo.Docs.Documents; namespace Volo.Docs.Projects @@ -51,7 +52,7 @@ namespace Volo.Docs.Projects var project = await _projectRepository.GetByShortNameAsync(shortName); var versions = await _versionCache.GetOrAddAsync( - project.ShortName, + CacheKeyGenerator.GenerateProjectVersionsCacheKey(project), () => GetVersionsAsync(project), () => new DistributedCacheEntryOptions { @@ -116,7 +117,7 @@ namespace Volo.Docs.Projects } return await LanguageCache.GetOrAddAsync( - project.ShortName, + CacheKeyGenerator.GenerateProjectLanguageCacheKey(project), GetLanguagesAsync, () => new DistributedCacheEntryOptions { diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Caching/CacheKeyGenerator.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Caching/CacheKeyGenerator.cs new file mode 100644 index 0000000000..6f53177658 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Caching/CacheKeyGenerator.cs @@ -0,0 +1,27 @@ +using Volo.Docs.Projects; + +namespace Volo.Docs.Caching +{ + public static class CacheKeyGenerator + { + public static string GenerateProjectLanguageCacheKey(Project project) + { + return project.ShortName; + } + + public static string GenerateProjectVersionsCacheKey(Project project) + { + return project.ShortName; + } + + public static string GenerateDocumentResourceCacheKey(Project project, string resourceName, string languageCode, string version) + { + return $"Resource@{project.ShortName}#{languageCode}#{resourceName}#{version}"; + } + + public static string GenerateDocumentUpdateInfoCacheKey(Project project, string documentName, string languageCode, string version) + { + return $"DocumentUpdateInfo{project.Id}#{documentName}#{languageCode}#{version}"; + } + } +} diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json index 6299a9aab2..452206632f 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json @@ -9,6 +9,9 @@ "Edit": "Edit", "LastEditTime": "Last edit", "Delete": "Delete", + "ClearCache": "Clear cache", + "ClearCacheConfirmationMessage": "Are you sure to clear all caches for project \"{0}\"", + "CacheNullExceptionMessage": "Cache is not available to clear!", "InThisDocument": "In this document", "GoToTop": "Go to top", "Projects": "Project(s)",