From fdc5976035ebbca9973a2fd8760b36b110a85f05 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 29 May 2014 18:58:51 +1000 Subject: [PATCH] Ensures the xml cache is distributed properly including variants as well as Examine updating correctly. --- .../Models/ContentVariations/VariantInfo.cs | 6 +- .../Cache/CacheRefresherEventHandler.cs | 1 + .../Cache/DistributedCacheExtensions.cs | 36 ++------ src/Umbraco.Web/Cache/PageCacheRefresher.cs | 83 ++++++++++++++++--- .../Cache/UnpublishedPageCacheRefresher.cs | 6 +- .../XmlPublishedCache/XmlPublishedContent.cs | 32 ++++--- src/Umbraco.Web/Search/ExamineEvents.cs | 12 ++- .../Publishing/UpdateCacheAfterPublish.cs | 14 +++- .../Publishing/UpdateCacheAfterUnPublish.cs | 8 +- 9 files changed, 130 insertions(+), 68 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentVariations/VariantInfo.cs b/src/Umbraco.Core/Models/ContentVariations/VariantInfo.cs index 54f01f5147..f509f1d120 100644 --- a/src/Umbraco.Core/Models/ContentVariations/VariantInfo.cs +++ b/src/Umbraco.Core/Models/ContentVariations/VariantInfo.cs @@ -19,7 +19,10 @@ namespace Umbraco.Core.Models.ContentVariations public VariantInfo(params int[] variantIds) { - VariantIds = variantIds; + VariantIds = variantIds.Any() + ? variantIds + : new int[] {}; + IsVariant = false; } @@ -28,6 +31,7 @@ namespace Umbraco.Core.Models.ContentVariations IsVariant = true; MasterDocId = masterDocId; Key = key; + VariantIds = new int[] { }; } public int[] VariantIds { get; set; } diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index f0a35b9057..82f9565180 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -266,6 +266,7 @@ namespace Umbraco.Web.Cache // newly published ones will be synced with the published page cache refresher var unpublished = e.SavedEntities.Where(x => x.JustPublished() == false).ToArray(); var ids = unpublished.Select(x => x.Id).ToList(); + //ensure the variants are refreshed too ids.AddRange(unpublished.SelectMany(x => x.VariantInfo.VariantIds)); ids.AddRange(unpublished.Select(x => x.VariantInfo.MasterDocId)); diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index a290aad3c1..b9aa9b4655 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -206,46 +206,28 @@ namespace Umbraco.Web.Cache dc.RefreshAll(new Guid(DistributedCache.PageCacheRefresherId)); } - /// - /// Refreshes the cache amongst servers for a page - /// - /// - /// - public static void RefreshPageCache(this DistributedCache dc, int documentId) - { - dc.Refresh(new Guid(DistributedCache.PageCacheRefresherId), documentId); - } - /// /// Refreshes page cache for all instances passed in /// /// - /// - public static void RefreshPageCache(this DistributedCache dc, params IContent[] content) + /// + public static void RefreshPageCache(this DistributedCache dc, params int[] contentIds) { - dc.Refresh(new Guid(DistributedCache.PageCacheRefresherId), x => x.Id, content); + dc.RefreshByJson(new Guid(DistributedCache.PageCacheRefresherId), + PageCacheRefresher.SerializeToJsonPayload(PageCacheRefresher.OperationType.Saved, contentIds)); } /// /// Removes the cache amongst servers for a page /// /// - /// - public static void RemovePageCache(this DistributedCache dc, params IContent[] content) + /// + public static void RemovePageCache(this DistributedCache dc, params int[] contentIds) { - dc.Remove(new Guid(DistributedCache.PageCacheRefresherId), x => x.Id, content); + dc.RefreshByJson(new Guid(DistributedCache.PageCacheRefresherId), + PageCacheRefresher.SerializeToJsonPayload(PageCacheRefresher.OperationType.Deleted, contentIds)); } - - /// - /// Removes the cache amongst servers for a page - /// - /// - /// - public static void RemovePageCache(this DistributedCache dc, int documentId) - { - dc.Remove(new Guid(DistributedCache.PageCacheRefresherId), documentId); - } - + /// /// invokes the unpublished page cache refresher /// diff --git a/src/Umbraco.Web/Cache/PageCacheRefresher.cs b/src/Umbraco.Web/Cache/PageCacheRefresher.cs index ca20d5c2de..50ca543be8 100644 --- a/src/Umbraco.Web/Cache/PageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/PageCacheRefresher.cs @@ -1,10 +1,12 @@ using System; +using System.Web.Script.Serialization; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; using Umbraco.Core.Sync; using umbraco; using umbraco.cms.businesslogic.web; +using System.Linq; namespace Umbraco.Web.Cache { @@ -15,7 +17,7 @@ namespace Umbraco.Web.Cache /// If Load balancing is enabled (by default disabled, is set in umbracoSettings.config) PageCacheRefresher will be called /// everytime content is added/updated/removed to ensure that the content cache is identical on all load balanced servers /// - public class PageCacheRefresher : TypedCacheRefresherBase + public class PageCacheRefresher : JsonCacheRefresherBase { protected override PageCacheRefresher Instance @@ -44,6 +46,51 @@ namespace Umbraco.Web.Cache get { return "Page Refresher"; } } + #region Static helpers + + /// + /// Converts the json to a JsonPayload object + /// + /// + /// + internal static JsonPayload[] DeserializeFromJsonPayload(string json) + { + var serializer = new JavaScriptSerializer(); + var jsonObject = serializer.Deserialize(json); + return jsonObject; + } + + + internal static string SerializeToJsonPayload(OperationType op, params int[] contentIds) + { + var serializer = new JavaScriptSerializer(); + var items = contentIds.Select(x => new JsonPayload + { + Id = x, + Operation = op + }).ToArray(); + var json = serializer.Serialize(items); + return json; + } + + #endregion + + #region Sub classes + + internal enum OperationType + { + Deleted, + Saved + } + + internal class JsonPayload + { + public int Id { get; set; } + public OperationType Operation { get; set; } + } + + #endregion + /// /// Refreshes all nodes in umbraco. /// @@ -79,22 +126,34 @@ namespace Umbraco.Web.Cache base.Remove(id); } - public override void Refresh(IContent instance) + /// + /// Implement the IJsonCacheRefresher so that we can bulk delete/refresh the cache based on multiple IDs + /// + /// + public override void Refresh(string jsonPayload) { - ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); - content.Instance.UpdateDocumentCache(new Document(instance)); - DistributedCache.Instance.ClearAllMacroCacheOnCurrentServer(); - DistributedCache.Instance.ClearXsltCacheOnCurrentServer(); - base.Refresh(instance); - } + var payloads = DeserializeFromJsonPayload(jsonPayload); + + if (payloads.Any() == false) return; - public override void Remove(IContent instance) - { ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); - content.Instance.ClearDocumentCache(new Document(instance)); DistributedCache.Instance.ClearAllMacroCacheOnCurrentServer(); DistributedCache.Instance.ClearXsltCacheOnCurrentServer(); - base.Remove(instance); + + foreach (var payload in payloads) + { + switch (payload.Operation) + { + case OperationType.Deleted: + content.Instance.ClearDocumentCache(payload.Id); + break; + case OperationType.Saved: + content.Instance.UpdateDocumentCache(payload.Id); + break; + } + } + + base.Refresh(jsonPayload); } } } diff --git a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs index 35cbcbe4bf..784c3a30de 100644 --- a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Cache /// /// A cache refresher used for non-published content, this is primarily to notify Examine indexes to update and to refresh the RuntimeCacheRefresher /// - public sealed class UnpublishedPageCacheRefresher : CacheRefresherBase, IJsonCacheRefresher + public sealed class UnpublishedPageCacheRefresher : JsonCacheRefresherBase { protected override UnpublishedPageCacheRefresher Instance { @@ -95,14 +95,14 @@ namespace Umbraco.Web.Cache /// Implement the IJsonCacheRefresher so that we can bulk delete/refresh the cache based on multiple IDs /// /// - public void Refresh(string jsonPayload) + public override void Refresh(string jsonPayload) { foreach (var payload in DeserializeFromJsonPayload(jsonPayload)) { RuntimeCacheProvider.Current.Delete(typeof(IContent), payload.Id); } - OnCacheUpdated(Instance, new CacheRefresherEventArgs(jsonPayload, MessageType.RefreshByJson)); + base.Refresh(jsonPayload); } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index 1941252265..d2f0755d5a 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -475,25 +475,21 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache //determine which child to add based on the current VariantInfo if (_variantInfo.IsVariant) { - //get the master, then children - if (xmlChild.ParentNode != null) - { - //TODO: There's probably a faster way with the old xml api to do this - - //find a variant of the current child for the current variant key - var variant = xmlChild.ParentNode.ChildNodes.OfType() - .FirstOrDefault(x => - //find all - x.AttributeValue("masterDocId") == xmlChild.AttributeValue("id") - && xmlChild.AttributeValue("variantKey") == _variantInfo.Key); + //TODO: There's probably a faster way with the old xml api to do this - if (variant != null) - { - //we found a variant! - _children.Add( - (new XmlPublishedContent(variant, _isPreviewing, true, _variantInfo)).CreateModel()); - useVariant = true; - } + //find a variant of the current child for the current variant key + var variant = workingNode.ChildNodes.OfType() + .FirstOrDefault(x => + //find all + x.AttributeValue("masterDocId") == xmlChild.AttributeValue("id") + && x.AttributeValue("variantKey") == _variantInfo.Key); + + if (variant != null) + { + //we found a variant! + _children.Add( + (new XmlPublishedContent(variant, _isPreviewing, true, _variantInfo)).CreateModel()); + useVariant = true; } } diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs index ed1e01f407..a84dad27b0 100644 --- a/src/Umbraco.Web/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/Search/ExamineEvents.cs @@ -342,10 +342,14 @@ namespace Umbraco.Web.Search DeleteIndexForEntity(payload.Id, false); break; - default: - throw new ArgumentOutOfRangeException(); - } - } + case UnpublishedPageCacheRefresher.OperationType.Saved: + + var c5 = ApplicationContext.Current.Services.ContentService.GetById(payload.Id); + ReIndexForContent(c5, false); + + break; + } + } } break; diff --git a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs index 1371c6fdb9..83c3c32634 100644 --- a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs +++ b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs @@ -62,7 +62,13 @@ namespace Umbraco.Web.Strategies.Publishing /// private void UpdateMultipleContentCache(IEnumerable content) { - DistributedCache.Instance.RefreshPageCache(content.ToArray()); + var asArray = content.ToArray(); + var ids = asArray.Select(x => x.Id).ToList(); + //ensure the variants are refreshed too + ids.AddRange(asArray.SelectMany(x => x.VariantInfo.VariantIds)); + ids.AddRange(asArray.Select(x => x.VariantInfo.MasterDocId)); + + DistributedCache.Instance.RefreshPageCache(ids.ToArray()); } /// @@ -70,7 +76,11 @@ namespace Umbraco.Web.Strategies.Publishing /// private void UpdateSingleContentCache(IContent content) { - DistributedCache.Instance.RefreshPageCache(content); + var ids = new List {content.Id}; + //ensure the variants are refreshed too + ids.AddRange(content.VariantInfo.VariantIds); + ids.Add(content.VariantInfo.MasterDocId); + DistributedCache.Instance.RefreshPageCache(ids.ToArray()); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs index 39ca0beda3..70fbc26ea9 100644 --- a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs +++ b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Umbraco.Core; using Umbraco.Core.Configuration; @@ -53,7 +54,12 @@ namespace Umbraco.Web.Strategies.Publishing /// private void UnPublishSingle(IContent content) { - DistributedCache.Instance.RemovePageCache(content); + var ids = new List { content.Id }; + //ensure the variants are refreshed too + ids.AddRange(content.VariantInfo.VariantIds); + ids.Add(content.VariantInfo.MasterDocId); + + DistributedCache.Instance.RemovePageCache(ids.ToArray()); } } } \ No newline at end of file