From b9ba350a2f209e9b2041ae87a29b66a1fa79cefe Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 29 Jul 2013 15:49:56 +1000 Subject: [PATCH] Updated the RebuildXmlStructures method with performance improvements. Added cache checking to the GetByPublishedVersion method since published content should always be 'latest' this will speed things up tremendously if items are found there. Added 2 more performance tests which show very large perf improvements, namely the Get_All_Published_Content_Of_Type shows a 77% improvement. --- .../Repositories/ContentRepository.cs | 10 +- .../Repositories/RepositoryBase.cs | 26 ++-- src/Umbraco.Core/Services/ContentService.cs | 41 ++++-- .../Services/PerformanceTests.cs | 130 ++++++++++++++++++ 4 files changed, 184 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 755fc5f04a..001705ebb9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -449,7 +449,15 @@ namespace Umbraco.Core.Persistence.Repositories foreach (var dto in dtos) { - yield return CreateContentFromDto(dto, dto.VersionId); + var fromCache = TryGetFromCache(dto.NodeId); + if (fromCache.Success) + { + yield return fromCache.Result; + } + else + { + yield return CreateContentFromDto(dto, dto.VersionId); + } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index cbd8b9d252..3967aef5e6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -85,11 +85,10 @@ namespace Umbraco.Core.Persistence.Repositories /// public TEntity Get(TId id) { - Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString()); - var rEntity = _cache.GetById(typeof(TEntity), key); - if (rEntity != null) + var fromCache = TryGetFromCache(id); + if (fromCache.Success) { - return (TEntity)rEntity; + return fromCache.Result; } var entity = PerformGet(id); @@ -112,6 +111,17 @@ namespace Umbraco.Core.Persistence.Repositories return entity; } + protected Attempt TryGetFromCache(TId id) + { + Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString()); + var rEntity = _cache.GetById(typeof(TEntity), key); + if (rEntity != null) + { + return new Attempt(true, (TEntity) rEntity); + } + return Attempt.False; + } + protected abstract IEnumerable PerformGetAll(params TId[] ids); /// /// Gets all entities of type TEntity or a list according to the passed in Ids @@ -173,14 +183,12 @@ namespace Umbraco.Core.Persistence.Repositories /// public bool Exists(TId id) { - Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString()); - var rEntity = _cache.GetById(typeof(TEntity), key); - if (rEntity != null) + var fromCache = TryGetFromCache(id); + if (fromCache.Success) { return true; } - - return PerformExists(id); + return PerformExists(id); } protected abstract int PerformCount(IQuery query); diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 83ff46d958..b47879bda1 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; @@ -250,6 +251,17 @@ namespace Umbraco.Core.Services } } + internal IEnumerable GetPublishedContentOfContentType(int id) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ContentTypeId == id); + var contents = repository.GetByPublishedVersion(query); + + return contents; + } + } + /// /// Gets a collection of objects by Level /// @@ -431,6 +443,19 @@ namespace Umbraco.Core.Services } } + /// + /// Gets all published content items + /// + /// + internal IEnumerable GetAllPublished() + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.Trashed == false); + return repository.GetByPublishedVersion(query); + } + } + /// /// Gets a collection of objects, which has an expiration date less than or equal to today. /// @@ -1359,23 +1384,13 @@ namespace Umbraco.Core.Services uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)"); - - //get all content items that are published - // Consider creating a Path query instead of recursive method: - // var query = Query.Builder.Where(x => x.Path.StartsWith("-1")); - var rootContent = GetRootContent(); - foreach (var content in rootContent.Where(content => content.Published)) - { - list.Add(content); - list.AddRange(GetPublishedDescendants(content)); - } + + list.AddRange(GetAllPublished()); } else { foreach (var id in contentTypeIds) { - - //first we'll clear out the data from the cmsContentXml table for this type uow.Database.Execute(@"delete from cmsContentXml where nodeId in (select cmsDocument.nodeId from cmsDocument @@ -1383,7 +1398,7 @@ namespace Umbraco.Core.Services where published = 1 and contentType = @contentTypeId)", new {contentTypeId = id}); //now get all published content objects of this type and add to the list - list.AddRange(GetContentOfContentType(id).Where(content => content.Published)); + list.AddRange(GetPublishedContentOfContentType(id)); } } diff --git a/src/Umbraco.Tests/Services/PerformanceTests.cs b/src/Umbraco.Tests/Services/PerformanceTests.cs index 892cc6f84d..e49d2a1da3 100644 --- a/src/Umbraco.Tests/Services/PerformanceTests.cs +++ b/src/Umbraco.Tests/Services/PerformanceTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Caching; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; @@ -66,6 +67,90 @@ namespace Umbraco.Tests.Services base.TearDown(); } + [Test] + public void Get_All_Published_Content() + { + var result = PrimeDbWithLotsOfContent(); + var contentSvc = (ContentService) ServiceContext.ContentService; + + var countOfPublished = result.Count(x => x.Published); + var contentTypeId = result.First().ContentTypeId; + + using (DisposableTimer.DebugDuration("Getting published content normally")) + { + //do this 10x! + for (var i = 0; i < 10; i++) + { + //clear the cache to make this test valid + RuntimeCacheProvider.Current.Clear(); + + var published = new List(); + //get all content items that are published + var rootContent = contentSvc.GetRootContent(); + foreach (var content in rootContent.Where(content => content.Published)) + { + published.Add(content); + published.AddRange(contentSvc.GetPublishedDescendants(content)); + } + Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); + } + + } + + using (DisposableTimer.DebugDuration("Getting published content optimized")) + { + + //do this 10x! + for (var i = 0; i < 10; i++) + { + //clear the cache to make this test valid + RuntimeCacheProvider.Current.Clear(); + + //get all content items that are published + var published = contentSvc.GetAllPublished(); + + Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); + } + } + } + + [Test] + public void Get_All_Published_Content_Of_Type() + { + var result = PrimeDbWithLotsOfContent(); + var contentSvc = (ContentService)ServiceContext.ContentService; + + var countOfPublished = result.Count(x => x.Published); + var contentTypeId = result.First().ContentTypeId; + + using (DisposableTimer.DebugDuration("Getting published content of type normally")) + { + //do this 10x! + for (var i = 0; i < 10; i++) + { + //clear the cache to make this test valid + RuntimeCacheProvider.Current.Clear(); + //get all content items that are published of this type + var published = contentSvc.GetContentOfContentType(contentTypeId).Where(content => content.Published); + Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); + } + } + + using (DisposableTimer.DebugDuration("Getting published content of type optimized")) + { + + //do this 10x! + for (var i = 0; i < 10; i++) + { + //clear the cache to make this test valid + RuntimeCacheProvider.Current.Clear(); + //get all content items that are published of this type + var published = contentSvc.GetPublishedContentOfContentType(contentTypeId); + Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); + } + } + } + [Test] public void Truncate_Insert_Vs_Update_Insert() { @@ -130,6 +215,51 @@ namespace Umbraco.Tests.Services } + private IEnumerable PrimeDbWithLotsOfContent() + { + var contentType1 = MockedContentTypes.CreateSimpleContentType(); + contentType1.AllowedAsRoot = true; + ServiceContext.ContentTypeService.Save(contentType1); + contentType1.AllowedContentTypes = new List + { + new ContentTypeSort + { + Alias = contentType1.Alias, + Id = new Lazy(() => contentType1.Id), + SortOrder = 0 + } + }; + var result = new List(); + ServiceContext.ContentTypeService.Save(contentType1); + IContent lastParent = MockedContent.CreateSimpleContent(contentType1); + ServiceContext.ContentService.SaveAndPublish(lastParent); + result.Add(lastParent); + //create 20 deep + for (var i = 0; i < 20; i++) + { + //for each level, create 20 + IContent content = null; + for (var j = 1; j <= 10; j++) + { + content = MockedContent.CreateSimpleContent(contentType1, "Name" + j, lastParent); + //only publish evens + if (j % 2 == 0) + { + ServiceContext.ContentService.SaveAndPublish(content); + } + else + { + ServiceContext.ContentService.Save(content); + } + result.Add(content); + } + + //assign the last one as the next parent + lastParent = content; + } + return result; + } + private IEnumerable PrimeDbWithLotsOfContentXmlRecords(Guid customObjectType) { var nodes = new List();