Skip cache refresher operations for content blueprints (#15633)

* Skip cache refresher operations for content blueprints

* Fix JsonPayload deserialization error by adding a default constructor and property initializers

* Obsolete JsonPayload constructor and update usages

(cherry picked from commit 3e28e10cdf)
This commit is contained in:
Ronald Barendse
2024-02-01 09:55:09 +01:00
committed by Bjarke Berg
parent 118ac8e230
commit e37cf30690
7 changed files with 232 additions and 56 deletions

View File

@@ -84,8 +84,8 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
isolatedCache.ClearOfType<IContent>((k, v) => v.Path?.Contains(pathid) ?? false);
}
// if the item is being completely removed, we need to refresh the domains cache if any domain was assigned to the content
if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.Remove))
// if the item is not a blueprint and is being completely removed, we need to refresh the domains cache if any domain was assigned to the content
if (payload.Blueprint is false && payload.ChangeTypes.HasTypesAny(TreeChangeTypes.Remove))
{
idsRemoved.Add(payload.Id);
}
@@ -120,7 +120,11 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
// should rename it, and then, this is only for Deploy, and then, ???
// if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache)
// ...
NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, payloads);
if (payloads.Any(x => x.Blueprint is false))
{
// Only notify if the payload contains actual (non-blueprint) contents
NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, payloads);
}
base.Refresh(payloads);
}
@@ -157,8 +161,13 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
}
}
// TODO (V14): Change into a record
public class JsonPayload
{
public JsonPayload()
{ }
[Obsolete("Use the default constructor and property initializers.")]
public JsonPayload(int id, Guid? key, TreeChangeTypes changeTypes)
{
Id = id;
@@ -166,11 +175,13 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCac
ChangeTypes = changeTypes;
}
public int Id { get; }
public int Id { get; init; }
public Guid? Key { get; }
public Guid? Key { get; init; }
public TreeChangeTypes ChangeTypes { get; }
public TreeChangeTypes ChangeTypes { get; init; }
public bool Blueprint { get; init; }
}
#endregion

View File

@@ -134,8 +134,14 @@ public sealed class LanguageCacheRefresher : PayloadCacheRefresherBase<LanguageC
ContentCacheRefresher.RefreshContentTypes(AppCaches); // we need to evict all IContent items
// now refresh all nucache
ContentCacheRefresher.JsonPayload[] clearContentPayload =
new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
ContentCacheRefresher.JsonPayload[] clearContentPayload = new[]
{
new ContentCacheRefresher.JsonPayload()
{
ChangeTypes = TreeChangeTypes.RefreshAll
}
};
ContentCacheRefresher.NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, clearContentPayload);
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Umbraco.
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Umbraco.Cms.Core.Cache;
@@ -132,7 +132,13 @@ public static class DistributedCacheExtensions
public static void RefreshAllContentCache(this DistributedCache dc)
{
ContentCacheRefresher.JsonPayload[] payloads = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
ContentCacheRefresher.JsonPayload[] payloads = new[]
{
new ContentCacheRefresher.JsonPayload()
{
ChangeTypes = TreeChangeTypes.RefreshAll
}
};
// note: refresh all content cache does refresh content types too
dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
@@ -145,8 +151,13 @@ public static class DistributedCacheExtensions
return;
}
IEnumerable<ContentCacheRefresher.JsonPayload> payloads = changes
.Select(x => new ContentCacheRefresher.JsonPayload(x.Item.Id, x.Item.Key, x.ChangeTypes));
IEnumerable<ContentCacheRefresher.JsonPayload> payloads = changes.Select(x => new ContentCacheRefresher.JsonPayload()
{
Id = x.Item.Id,
Key = x.Item.Key,
ChangeTypes = x.ChangeTypes,
Blueprint = x.Item.Blueprint
});
dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
}

View File

@@ -51,13 +51,15 @@ public sealed class ContentIndexingNotificationHandler : INotificationHandler<Co
foreach (ContentCacheRefresher.JsonPayload payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject)
{
if (payload.Blueprint)
{
// Skip blueprints
continue;
}
if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove))
{
if (deleteBatch == null)
{
deleteBatch = new HashSet<int>();
}
deleteBatch ??= new HashSet<int>();
deleteBatch.Add(payload.Id);
}
else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))

View File

@@ -210,7 +210,16 @@ internal class PublishedSnapshotService : IPublishedSnapshotService
// they require.
using (_contentStore.GetScopedWriteLock(_scopeProvider))
{
NotifyLocked(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _);
NotifyLocked(
new[]
{
new ContentCacheRefresher.JsonPayload()
{
ChangeTypes = TreeChangeTypes.RefreshAll
}
},
out _,
out _);
}
using (_mediaStore.GetScopedWriteLock(_scopeProvider))
@@ -852,6 +861,12 @@ internal class PublishedSnapshotService : IPublishedSnapshotService
// contentStore is write-locked during changes - see note above, calls to this method are wrapped in contentStore.GetScopedWriteLock
foreach (ContentCacheRefresher.JsonPayload payload in payloads)
{
if (payload.Blueprint)
{
// Skip blueprints
continue;
}
_logger.LogDebug("Notified {ChangeTypes} for content {ContentId}", payload.ChangeTypes, payload.Id);
if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))

View File

@@ -1,7 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using Newtonsoft.Json;
using NUnit.Framework;
using Umbraco.Cms.Core.Cache;
@@ -19,8 +18,10 @@ public class RefresherTests
{
new MediaCacheRefresher.JsonPayload(1234, Guid.NewGuid(), TreeChangeTypes.None),
};
var json = JsonConvert.SerializeObject(source);
var payload = JsonConvert.DeserializeObject<MediaCacheRefresher.JsonPayload[]>(json);
Assert.AreEqual(source[0].Id, payload[0].Id);
Assert.AreEqual(source[0].Key, payload[0].Key);
Assert.AreEqual(source[0].ChangeTypes, payload[0].ChangeTypes);
@@ -31,13 +32,21 @@ public class RefresherTests
{
ContentCacheRefresher.JsonPayload[] source =
{
new ContentCacheRefresher.JsonPayload(1234, Guid.NewGuid(), TreeChangeTypes.None),
new ContentCacheRefresher.JsonPayload()
{
Id = 1234,
Key = Guid.NewGuid(),
ChangeTypes = TreeChangeTypes.None
}
};
var json = JsonConvert.SerializeObject(source);
var payload = JsonConvert.DeserializeObject<ContentCacheRefresher.JsonPayload[]>(json);
Assert.AreEqual(source[0].Id, payload[0].Id);
Assert.AreEqual(source[0].Key, payload[0].Key);
Assert.AreEqual(source[0].ChangeTypes, payload[0].ChangeTypes);
Assert.AreEqual(source[0].Blueprint, payload[0].Blueprint);
}
[Test]
@@ -47,8 +56,10 @@ public class RefresherTests
{
new ContentTypeCacheRefresher.JsonPayload("xxx", 1234, ContentTypeChangeTypes.None),
};
var json = JsonConvert.SerializeObject(source);
var payload = JsonConvert.DeserializeObject<ContentTypeCacheRefresher.JsonPayload[]>(json);
Assert.AreEqual(source[0].ItemType, payload[0].ItemType);
Assert.AreEqual(source[0].Id, payload[0].Id);
Assert.AreEqual(source[0].ChangeTypes, payload[0].ChangeTypes);
@@ -61,8 +72,10 @@ public class RefresherTests
{
new DataTypeCacheRefresher.JsonPayload(1234, Guid.NewGuid(), true),
};
var json = JsonConvert.SerializeObject(source);
var payload = JsonConvert.DeserializeObject<DataTypeCacheRefresher.JsonPayload[]>(json);
Assert.AreEqual(source[0].Id, payload[0].Id);
Assert.AreEqual(source[0].Key, payload[0].Key);
Assert.AreEqual(source[0].Removed, payload[0].Removed);
@@ -71,10 +84,14 @@ public class RefresherTests
[Test]
public void DomainCacheRefresherCanDeserializeJsonPayload()
{
DomainCacheRefresher.JsonPayload[]
source = { new DomainCacheRefresher.JsonPayload(1234, DomainChangeTypes.None) };
DomainCacheRefresher.JsonPayload[] source =
{
new DomainCacheRefresher.JsonPayload(1234, DomainChangeTypes.None)
};
var json = JsonConvert.SerializeObject(source);
var payload = JsonConvert.DeserializeObject<DomainCacheRefresher.JsonPayload[]>(json);
Assert.AreEqual(source[0].Id, payload[0].Id);
Assert.AreEqual(source[0].ChangeType, payload[0].ChangeType);
}

View File

@@ -1,6 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Models;
@@ -359,7 +356,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
// notify
SnapshotService.Notify(
new[] { new ContentCacheRefresher.JsonPayload(10, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _);
new[]
{
new ContentCacheRefresher.JsonPayload()
{
Id = 10,
ChangeTypes = TreeChangeTypes.RefreshBranch
}
},
out _,
out _);
// changes that *I* make are immediately visible on the current snapshot
var documents = snapshot.Content.GetAtRoot().ToArray();
@@ -393,7 +399,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
// notify
SnapshotService.Notify(
new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _);
new[]
{
new ContentCacheRefresher.JsonPayload()
{
Id = 1,
ChangeTypes = TreeChangeTypes.RefreshBranch
}
},
out _,
out _);
// changes that *I* make are immediately visible on the current snapshot
var documents = snapshot.Content.GetAtRoot().ToArray();
@@ -451,7 +466,11 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
SnapshotService.Notify(
new[]
{
new ContentCacheRefresher.JsonPayload(kit.Node.ParentContentId, Guid.Empty, TreeChangeTypes.RefreshBranch),
new ContentCacheRefresher.JsonPayload()
{
Id = kit.Node.ParentContentId,
ChangeTypes = TreeChangeTypes.RefreshBranch
}
},
out _,
out _);
@@ -517,11 +536,19 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
// notify
SnapshotService.Notify(
new[]
{
// removal must come first
new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.RefreshBranch),
new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch),
},
{
// removal must come first
new ContentCacheRefresher.JsonPayload()
{
Id = 2,
ChangeTypes = TreeChangeTypes.RefreshBranch
},
new ContentCacheRefresher.JsonPayload()
{
Id = 1,
ChangeTypes = TreeChangeTypes.RefreshBranch
}
},
out _,
out _);
@@ -572,7 +599,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
// notify - which ensures there are 2 generations in the cache meaning each LinkedNode has a Next value.
SnapshotService.Notify(
new[] { new ContentCacheRefresher.JsonPayload(4, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _);
new[]
{
new ContentCacheRefresher.JsonPayload()
{
Id = 4,
ChangeTypes = TreeChangeTypes.RefreshBranch
}
},
out _,
out _);
// refresh the branch again, this used to show the issue where a null ref exception would occur
// because in the ClearBranchLocked logic, when SetValueLocked was called within a recursive call
@@ -580,7 +616,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
// this value before recursing.
Assert.DoesNotThrow(() =>
SnapshotService.Notify(
new[] { new ContentCacheRefresher.JsonPayload(4, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _));
new[]
{
new ContentCacheRefresher.JsonPayload()
{
Id = 4,
ChangeTypes = TreeChangeTypes.RefreshBranch
}
},
out _,
out _));
}
[Test]
@@ -760,11 +805,23 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
// notify
SnapshotService.Notify(
new[]
{
new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.Remove), // remove last
new ContentCacheRefresher.JsonPayload(5, Guid.Empty, TreeChangeTypes.Remove), // remove middle
new ContentCacheRefresher.JsonPayload(9, Guid.Empty, TreeChangeTypes.Remove), // remove first
},
{
new ContentCacheRefresher.JsonPayload() // remove last
{
Id = 3,
ChangeTypes = TreeChangeTypes.Remove
},
new ContentCacheRefresher.JsonPayload() // remove middle
{
Id = 5,
ChangeTypes = TreeChangeTypes.Remove
},
new ContentCacheRefresher.JsonPayload() // remove first
{
Id = 9,
ChangeTypes = TreeChangeTypes.Remove
}
},
out _,
out _);
@@ -780,11 +837,23 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
// notify
SnapshotService.Notify(
new[]
{
new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.Remove), // remove first
new ContentCacheRefresher.JsonPayload(8, Guid.Empty, TreeChangeTypes.Remove), // remove
new ContentCacheRefresher.JsonPayload(7, Guid.Empty, TreeChangeTypes.Remove), // remove
},
{
new ContentCacheRefresher.JsonPayload() // remove first
{
Id = 1,
ChangeTypes = TreeChangeTypes.Remove
},
new ContentCacheRefresher.JsonPayload() // remove
{
Id = 8,
ChangeTypes = TreeChangeTypes.Remove
},
new ContentCacheRefresher.JsonPayload() // remove
{
Id = 7,
ChangeTypes = TreeChangeTypes.Remove
}
},
out _,
out _);
@@ -824,8 +893,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
SnapshotService.Notify(
new[]
{
new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch),
new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.RefreshNode),
new ContentCacheRefresher.JsonPayload()
{
Id = 1,
ChangeTypes = TreeChangeTypes.RefreshBranch
},
new ContentCacheRefresher.JsonPayload()
{
Id = 2,
ChangeTypes = TreeChangeTypes.RefreshNode
}
},
out _,
out _);
@@ -888,7 +965,17 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
var parentNode = parentNodes[0];
AssertLinkedNode(parentNode.contentNode, -1, -1, -1, 2, 2);
SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.Remove) }, out _, out _);
SnapshotService.Notify(
new[]
{
new ContentCacheRefresher.JsonPayload()
{
Id = 2,
ChangeTypes = TreeChangeTypes.Remove
}
},
out _,
out _);
parentNodes = contentStore.Test.GetValues(1);
parentNode = parentNodes[0];
@@ -945,9 +1032,13 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
SnapshotService.Notify(
new[]
{
new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.Remove), // remove middle child
},
{
new ContentCacheRefresher.JsonPayload() // remove middle child
{
Id = 3,
ChangeTypes = TreeChangeTypes.Remove
}
},
out _,
out _);
@@ -1014,7 +1105,16 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
Assert.IsFalse(contentStore.Test.NextGen);
SnapshotService.Notify(
new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshNode) }, out _, out _);
new[]
{
new ContentCacheRefresher.JsonPayload()
{
Id = 1,
ChangeTypes = TreeChangeTypes.RefreshNode
}
},
out _,
out _);
Assert.AreEqual(2, contentStore.Test.LiveGen);
Assert.IsTrue(contentStore.Test.NextGen);
@@ -1085,7 +1185,17 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
published ? rootKit.PublishedData : null);
NuCacheContentService.ContentKits[1] = kit;
SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, changeType) }, out _, out _);
SnapshotService.Notify(
new[]
{
new ContentCacheRefresher.JsonPayload()
{
Id = 1,
ChangeTypes = changeType
}
},
out _,
out _);
Assert.AreEqual(assertGen, contentStore.Test.LiveGen);
Assert.IsTrue(contentStore.Test.NextGen);
@@ -1163,9 +1273,13 @@ public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceT
SnapshotService.Notify(
new[]
{
new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.RefreshBranch), // remove middle child
},
{
new ContentCacheRefresher.JsonPayload() // remove middle child
{
Id = 3,
ChangeTypes = TreeChangeTypes.RefreshBranch
}
},
out _,
out _);