From dbda6689b68bb429e8463e7a35b5d1811e37fc0a Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 5 Sep 2017 14:51:56 +0200 Subject: [PATCH] perfs - suspendable --- src/Umbraco.Core/Umbraco.Core.csproj | 3 + .../Cache/CacheRefresherEventHandler.cs | 2 + src/Umbraco.Web/Cache/PageCacheRefresher.cs | 41 +++---- .../Scheduling/ScheduledPublishing.cs | 3 + src/Umbraco.Web/Search/ExamineEvents.cs | 88 +++++++------ src/Umbraco.Web/Suspendable.cs | 116 ++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../umbraco.presentation/content.cs | 1 + 8 files changed, 196 insertions(+), 59 deletions(-) create mode 100644 src/Umbraco.Web/Suspendable.cs diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 8854c3c4ab..77e84da945 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -134,6 +134,9 @@ + + ..\Umbraco.Web.UI\Bin\umbraco.dll + diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index beab3e6cf0..6fcdf2fcf3 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -823,6 +823,8 @@ namespace Umbraco.Web.Cache var handler = FindHandler(e); if (handler == null) continue; + ApplicationContext.Current.ProfilingLogger.Logger.Info("Handling " + e.Sender + " " + e.EventName); + handler.Invoke(null, new[] { e.Sender, e.Args }); } } diff --git a/src/Umbraco.Web/Cache/PageCacheRefresher.cs b/src/Umbraco.Web/Cache/PageCacheRefresher.cs index e884c9b3b8..d9bb7e9b88 100644 --- a/src/Umbraco.Web/Cache/PageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/PageCacheRefresher.cs @@ -50,8 +50,9 @@ namespace Umbraco.Web.Cache /// public override void RefreshAll() { - content.Instance.RefreshContentFromDatabase(); - XmlPublishedContent.ClearRequest(); + if (Suspendable.PageCacheRefresher.CanRefreshDocumentCacheFromDatabase) + content.Instance.RefreshContentFromDatabase(); + ClearCaches(); base.RefreshAll(); } @@ -61,11 +62,9 @@ namespace Umbraco.Web.Cache /// The id. public override void Refresh(int id) { - ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); - content.Instance.UpdateDocumentCache(id); - XmlPublishedContent.ClearRequest(); - DistributedCache.Instance.ClearAllMacroCacheOnCurrentServer(); - DistributedCache.Instance.ClearXsltCacheOnCurrentServer(); + if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache) + content.Instance.UpdateDocumentCache(id); + ClearCaches(); base.Refresh(id); } @@ -75,35 +74,35 @@ namespace Umbraco.Web.Cache /// The id. public override void Remove(int id) { - ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); - content.Instance.ClearDocumentCache(id, false); - XmlPublishedContent.ClearRequest(); - DistributedCache.Instance.ClearAllMacroCacheOnCurrentServer(); - DistributedCache.Instance.ClearXsltCacheOnCurrentServer(); - ClearAllIsolatedCacheByEntityType(); + if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache) + content.Instance.ClearDocumentCache(id, false); + ClearCaches(); base.Remove(id); } public override void Refresh(IContent instance) { - ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); - content.Instance.UpdateDocumentCache(new Document(instance)); - XmlPublishedContent.ClearRequest(); - DistributedCache.Instance.ClearAllMacroCacheOnCurrentServer(); - DistributedCache.Instance.ClearXsltCacheOnCurrentServer(); - ClearAllIsolatedCacheByEntityType(); + if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache) + content.Instance.UpdateDocumentCache(new Document(instance)); + ClearCaches(); base.Refresh(instance); } public override void Remove(IContent instance) + { + if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache) + content.Instance.ClearDocumentCache(new Document(instance), false); + ClearCaches(); + base.Remove(instance); + } + + private void ClearCaches() { ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); - content.Instance.ClearDocumentCache(new Document(instance), false); XmlPublishedContent.ClearRequest(); DistributedCache.Instance.ClearAllMacroCacheOnCurrentServer(); DistributedCache.Instance.ClearXsltCacheOnCurrentServer(); ClearAllIsolatedCacheByEntityType(); - base.Remove(instance); } } } diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs index 24359d2b50..96cf724d02 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs @@ -27,6 +27,9 @@ namespace Umbraco.Web.Scheduling { if (_appContext == null) return true; // repeat... + if (Suspendable.ScheduledPublishing.CanRun == false) + return true; // repeat, later + switch (_appContext.GetCurrentServerRole()) { case ServerRole.Slave: diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs index 7fbbf29b89..e018bac214 100644 --- a/src/Umbraco.Web/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/Search/ExamineEvents.cs @@ -24,7 +24,6 @@ namespace Umbraco.Web.Search /// public sealed class ExamineEvents : ApplicationEventHandler { - /// /// Once the application has started we should bind to all events and initialize the providers. /// @@ -32,9 +31,9 @@ namespace Umbraco.Web.Search /// /// /// We need to do this on the Started event as to guarantee that all resolvers are setup properly. - /// + /// protected override void ApplicationStarted(UmbracoApplicationBase httpApplication, ApplicationContext applicationContext) - { + { LogHelper.Info("Initializing Examine and binding to business logic events"); var registeredProviders = ExamineManager.Instance.IndexProviderCollection @@ -46,14 +45,14 @@ namespace Umbraco.Web.Search if (registeredProviders == 0) return; - //Bind to distributed cache events - this ensures that this logic occurs on ALL servers that are taking part + //Bind to distributed cache events - this ensures that this logic occurs on ALL servers that are taking part // in a load balanced environment. CacheRefresherBase.CacheUpdated += UnpublishedPageCacheRefresherCacheUpdated; CacheRefresherBase.CacheUpdated += PublishedPageCacheRefresherCacheUpdated; CacheRefresherBase.CacheUpdated += MediaCacheRefresherCacheUpdated; CacheRefresherBase.CacheUpdated += MemberCacheRefresherCacheUpdated; CacheRefresherBase.CacheUpdated += ContentTypeCacheRefresherCacheUpdated; - + var contentIndexer = ExamineManager.Instance.IndexProviderCollection[Constants.Examine.InternalIndexer] as UmbracoContentIndexer; if (contentIndexer != null) { @@ -77,6 +76,9 @@ namespace Umbraco.Web.Search /// static void ContentTypeCacheRefresherCacheUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs e) { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + var indexersToUpdated = ExamineManager.Instance.IndexProviderCollection.OfType(); foreach (var provider in indexersToUpdated) { @@ -114,7 +116,7 @@ namespace Umbraco.Web.Search } } - //TODO: We need to update Examine to support re-indexing multiple items at once instead of one by one which will speed up + //TODO: We need to update Examine to support re-indexing multiple items at once instead of one by one which will speed up // the re-indexing process, we don't want to revert to rebuilding the whole thing! if (contentTypesChanged.Count > 0) @@ -129,8 +131,8 @@ namespace Umbraco.Web.Search { ReIndexForContent(contentItem, contentItem.HasPublishedVersion && contentItem.Trashed == false); } - } - } + } + } } if (mediaTypesChanged.Count > 0) { @@ -163,11 +165,14 @@ namespace Umbraco.Web.Search } } } - + } static void MemberCacheRefresherCacheUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs e) { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + switch (e.MessageType) { case MessageType.RefreshById: @@ -215,6 +220,9 @@ namespace Umbraco.Web.Search /// static void MediaCacheRefresherCacheUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs e) { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + switch (e.MessageType) { case MessageType.RefreshById: @@ -252,13 +260,13 @@ namespace Umbraco.Web.Search if (media1 != null) { ReIndexForMedia(media1, media1.Trashed == false); - } + } break; case MediaCacheRefresher.OperationType.Trashed: - + //keep if trashed for indexes supporting unpublished //(delete the index from all indexes not supporting unpublished content) - + DeleteIndexForEntity(payload.Id, true); //We then need to re-index this item for all indexes supporting unpublished content @@ -272,20 +280,20 @@ namespace Umbraco.Web.Search case MediaCacheRefresher.OperationType.Deleted: //permanently remove from all indexes - + DeleteIndexForEntity(payload.Id, false); break; default: throw new ArgumentOutOfRangeException(); - } - } + } + } } break; - case MessageType.RefreshByInstance: - case MessageType.RemoveByInstance: - case MessageType.RefreshAll: + case MessageType.RefreshByInstance: + case MessageType.RemoveByInstance: + case MessageType.RefreshAll: default: //We don't support these, these message types will not fire for media break; @@ -302,6 +310,9 @@ namespace Umbraco.Web.Search /// static void PublishedPageCacheRefresherCacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs e) { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + switch (e.MessageType) { case MessageType.RefreshById: @@ -312,8 +323,8 @@ namespace Umbraco.Web.Search } break; case MessageType.RemoveById: - - //This is triggered when the item has been unpublished or trashed (which also performs an unpublish). + + //This is triggered when the item has been unpublished or trashed (which also performs an unpublish). var c2 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject); if (c2 != null) @@ -368,6 +379,9 @@ namespace Umbraco.Web.Search /// static void UnpublishedPageCacheRefresherCacheUpdated(UnpublishedPageCacheRefresher sender, CacheRefresherEventArgs e) { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + switch (e.MessageType) { case MessageType.RefreshById: @@ -378,9 +392,9 @@ namespace Umbraco.Web.Search } break; case MessageType.RemoveById: - + // This is triggered when the item is permanently deleted - + DeleteIndexForEntity((int)e.MessageObject, false); break; case MessageType.RefreshByInstance: @@ -399,7 +413,7 @@ namespace Umbraco.Web.Search { DeleteIndexForEntity(c4.Id, false); } - break; + break; case MessageType.RefreshByJson: var jsonPayloads = UnpublishedPageCacheRefresher.DeserializeFromJsonPayload((string)e.MessageObject); @@ -409,29 +423,28 @@ namespace Umbraco.Web.Search { switch (payload.Operation) { - case UnpublishedPageCacheRefresher.OperationType.Deleted: + case UnpublishedPageCacheRefresher.OperationType.Deleted: //permanently remove from all indexes - + DeleteIndexForEntity(payload.Id, false); break; default: throw new ArgumentOutOfRangeException(); - } - } + } + } } break; - case MessageType.RefreshAll: + case MessageType.RefreshAll: default: //We don't support these, these message types will not fire for unpublished content break; } } - private static void ReIndexForMember(IMember member) { ExamineManager.Instance.ReIndexNode( @@ -447,7 +460,7 @@ namespace Umbraco.Web.Search /// /// /// - + private static void IndexerDocumentWriting(object sender, DocumentWritingEventArgs e) { if (e.Fields.Keys.Contains("nodeName")) @@ -463,7 +476,7 @@ namespace Umbraco.Web.Search )); } } - + private static void ReIndexForMedia(IMedia sender, bool isMediaPublished) { var xml = sender.ToXml(); @@ -497,7 +510,7 @@ namespace Umbraco.Web.Search //if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, // otherwise if keepIfUnpublished == false then remove from all indexes - + .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false) .Where(x => x.EnableDefaultEventHandler)); } @@ -518,7 +531,7 @@ namespace Umbraco.Web.Search ExamineManager.Instance.ReIndexNode( xml, IndexTypes.Content, ExamineManager.Instance.IndexProviderCollection.OfType() - + //Index this item for all indexers if the content is published, otherwise if the item is not published // then only index this for indexers supporting unpublished content @@ -531,10 +544,10 @@ namespace Umbraco.Web.Search /// /// /// true if data is going to be returned from cache - /// + /// [Obsolete("This method is no longer used and will be removed from the core in future versions, the cacheOnly parameter has no effect. Use the other ToXDocument overload instead")] public static XDocument ToXDocument(Content node, bool cacheOnly) - { + { return ToXDocument(node); } @@ -542,7 +555,7 @@ namespace Umbraco.Web.Search /// Converts a content node to Xml /// /// - /// + /// private static XDocument ToXDocument(Content node) { if (TypeHelper.IsTypeAssignableFrom(node)) @@ -561,7 +574,7 @@ namespace Umbraco.Web.Search if (xNode.Attributes["nodeTypeAlias"] == null) { - //we'll add the nodeTypeAlias ourselves + //we'll add the nodeTypeAlias ourselves XmlAttribute d = xDoc.CreateAttribute("nodeTypeAlias"); d.Value = node.ContentType.Alias; xNode.Attributes.Append(d); @@ -569,6 +582,5 @@ namespace Umbraco.Web.Search return new XDocument(ExamineXmlExtensions.ToXElement(xNode)); } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Suspendable.cs b/src/Umbraco.Web/Suspendable.cs new file mode 100644 index 0000000000..29e8c3a540 --- /dev/null +++ b/src/Umbraco.Web/Suspendable.cs @@ -0,0 +1,116 @@ +using System; +using System.Diagnostics; +using Examine; +using Examine.Providers; +using Umbraco.Core; +using Umbraco.Web.Cache; + +namespace Umbraco.Web +{ + public static class Suspendable + { + public static class PageCacheRefresher + { + private static bool _tried, _suspended; + + public static bool CanRefreshDocumentCacheFromDatabase + { + get + { + // trying a full refresh + if (_suspended == false) return true; + _tried = true; // remember we tried + return false; + } + } + + public static bool CanUpdateDocumentCache + { + get + { + // trying a partial update + // ok if not suspended, or if we haven't done a full already + return _suspended == false || _tried == false; + } + } + + public static void SuspendDocumentCache() + { + ApplicationContext.Current.ProfilingLogger.Logger.Info(typeof (PageCacheRefresher), "Suspend document cache."); + _suspended = true; + } + + public static void ResumeDocumentCache() + { + _suspended = false; + + ApplicationContext.Current.ProfilingLogger.Logger.Info(typeof (PageCacheRefresher), string.Format("Resume document cache (reload:{0}).", _tried ? "true" : "false")); + + if (_tried == false) return; + _tried = false; + + var pageRefresher = CacheRefreshersResolver.Current.GetById(new Guid(DistributedCache.PageCacheRefresherId)); + pageRefresher.RefreshAll(); + } + } + + public static class ExamineEvents + { + private static bool _tried, _suspended; + + public static bool CanIndex + { + get + { + if (_suspended == false) return true; + _tried = true; // remember we tried + return false; + } + } + + public static void SuspendIndexers() + { + ApplicationContext.Current.ProfilingLogger.Logger.Info(typeof (ExamineEvents), "Suspend indexers."); + _suspended = true; + } + + public static void ResumeIndexers() + { + _suspended = false; + + ApplicationContext.Current.ProfilingLogger.Logger.Info(typeof (ExamineEvents), string.Format("Resume indexers (rebuild:{0}).", _tried ? "true" : "false")); + + if (_tried == false) return; + _tried = false; + + // fixme - could we fork this on a background thread? + foreach (BaseIndexProvider indexer in ExamineManager.Instance.IndexProviderCollection) + { + indexer.RebuildIndex(); + } + } + } + + public static class ScheduledPublishing + { + private static bool _suspended; + + public static bool CanRun + { + get { return _suspended == false; } + } + + public static void Suspend() + { + ApplicationContext.Current.ProfilingLogger.Logger.Info(typeof (ScheduledPublishing), "Suspend scheduled publishing."); + _suspended = true; + } + + public static void Resume() + { + ApplicationContext.Current.ProfilingLogger.Logger.Info(typeof (ScheduledPublishing), "Resume scheduled publishing."); + _suspended = false; + } + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ea3a0dfa7c..f8d6f9080e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -435,6 +435,7 @@ + diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index 54bb1ac2f1..a7d40fa088 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Text;