Skip to content

Commit

Permalink
Handle only single content type event to avoid reloading the content …
Browse files Browse the repository at this point in the history
…and media cache multiple times after a deployment containing more than one changed document type.
  • Loading branch information
AndyButland authored and nul800sebastiaan committed Jun 15, 2021
1 parent e70c362 commit 348d167
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 4 deletions.
16 changes: 15 additions & 1 deletion src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Changes;
using Umbraco.Tests.Testing;
using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web;
Expand Down Expand Up @@ -148,7 +149,6 @@ public void CanHandleEvent()
{
// works because that event definition maps to an empty handler
new EventDefinition<IContentTypeService, SaveEventArgs<IContentType>>(null, Current.Services.ContentTypeService, new SaveEventArgs<IContentType>(Enumerable.Empty<IContentType>()), "Saved"),

};

var umbracoContextFactory = new UmbracoContextFactory(
Expand All @@ -166,5 +166,19 @@ public void CanHandleEvent()
var refreshers = new DistributedCacheBinder(null, umbracoContextFactory, null);
refreshers.HandleEvents(definitions);
}

[Test]
public void OnlyHandlesOnContentTypeEvent()
{
var definitions = new IEventDefinition[]
{
new EventDefinition<IContentTypeService, ContentTypeChange<IContentType>.EventArgs>(null, Current.Services.ContentTypeService, new ContentTypeChange<IContentType>.EventArgs(Enumerable.Empty<ContentTypeChange<IContentType>>()), "Changed"),
new EventDefinition<IContentTypeService, SaveEventArgs<IContentType>>(null, Current.Services.ContentTypeService, new SaveEventArgs<IContentType>(Enumerable.Empty<IContentType>()), "Saved"),
new EventDefinition<IContentTypeService, ContentTypeChange<IContentType>.EventArgs>(null, Current.Services.ContentTypeService, new ContentTypeChange<IContentType>.EventArgs(Enumerable.Empty<ContentTypeChange<IContentType>>()), "Changed"),
new EventDefinition<IContentTypeService, SaveEventArgs<IContentType>>(null, Current.Services.ContentTypeService, new SaveEventArgs<IContentType>(Enumerable.Empty<IContentType>()), "Saved"),
};
var result = DistributedCacheBinder.GetReducedEventList(definitions);
Assert.AreEqual(1, result.Count());
}
}
}
54 changes: 51 additions & 3 deletions src/Umbraco.Web/Cache/DistributedCacheBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,15 @@ internal static MethodInfo FindHandler(IEventDefinition eventDefinition)
/// <inheritdoc />
public void HandleEvents(IEnumerable<IEventDefinition> events)
{
// ensure we run with an UmbracoContext, because this may run in a background task,
// yet developers may be using the 'current' UmbracoContext in the event handlers
// Ensure we run with an UmbracoContext, because this may run in a background task,
// yet developers may be using the 'current' UmbracoContext in the event handlers.
using (_umbracoContextFactory.EnsureUmbracoContext())
{
foreach (var e in events)
// When it comes to content types types, a change to any single one will trigger a reload of the content and media caches.
// As far as I (AB) can tell, there's no type specific logic here, they all clear caches for all content types, and trigger a reload of all content and media.
// We also have events registered for Changed and Saved, which do the same thing, so really only need one of these.
// Hence if we have more than one document or media types, we can and should only handle one of the events for one, to avoid repeated cache reloads.
foreach (var e in GetReducedEventList(events))
{
var handler = FindHandler(e);
if (handler == null)
Expand All @@ -80,5 +84,49 @@ public void HandleEvents(IEnumerable<IEventDefinition> events)
}
}
}

// Internal for tests
internal static IEnumerable<IEventDefinition> GetReducedEventList(IEnumerable<IEventDefinition> events)
{
var reducedEvents = new List<IEventDefinition>();

var gotDoumentType = false;
var gotMediaType = false;
var gotMemberType = false;

foreach (var evt in events)
{
if (evt.Sender.ToString().Contains(nameof(Core.Services.Implement.ContentTypeService)))
{
if (gotDoumentType == false)
{
reducedEvents.Add(evt);
gotDoumentType = true;
}
}
else if (evt.Sender.ToString().Contains(nameof(Core.Services.Implement.MediaTypeService)))
{
if (gotMediaType == false)
{
reducedEvents.Add(evt);
gotMediaType = true;
}
}
else if (evt.Sender.ToString().Contains(nameof(Core.Services.Implement.MemberTypeService)))
{
if (gotMemberType == false)
{
reducedEvents.Add(evt);
gotMemberType = true;
}
}
else
{
reducedEvents.Add(evt);
}
}

return reducedEvents;
}
}
}

0 comments on commit 348d167

Please sign in to comment.