Skip to content

Commit d03bc74

Browse files
authored
Improve handling for out of process search providers (take 2) (#97)
* Add origin to improve handling for out-of-process search providers * Extend origin handling to structural changes * Move database index cache clean-up to the instance bound notification handlers. * Review changes
1 parent 12e95b0 commit d03bc74

58 files changed

Lines changed: 887 additions & 223 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Umbraco.Cms.Search.Core/Cache/Content/DraftContentCacheRefresher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace Umbraco.Cms.Search.Core.Cache.Content;
77

8-
internal sealed class DraftContentCacheRefresher : PayloadCacheRefresherBase<DraftContentCacheRefresherNotification, DraftContentCacheRefresher.JsonPayload>
8+
internal sealed class DraftContentCacheRefresher : PayloadCacheRefresherBase<DraftContentCacheRefresherNotification, ContentCacheRefresherNotificationPayload<DraftContentCacheRefresher.JsonPayload>>
99
{
1010
public static readonly Guid UniqueId = Guid.Parse("4DA581BA-07B8-4643-945E-FA9687C14D15");
1111

src/Umbraco.Cms.Search.Core/Cache/Content/DraftContentNotificationHandler.cs

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,41 @@
33
using Umbraco.Cms.Core.Models;
44
using Umbraco.Cms.Core.Notifications;
55
using Umbraco.Cms.Core.Services.Changes;
6+
using Umbraco.Cms.Search.Core.Services.ContentIndexing;
67

78
namespace Umbraco.Cms.Search.Core.Cache.Content;
89

9-
internal sealed class DraftContentNotificationHandler : ContentNotificationHandlerBase,
10+
internal sealed class DraftContentNotificationHandler : ContentNotificationHandlerBase<DraftContentCacheRefresher.JsonPayload>,
1011
IDistributedCacheNotificationHandler<ContentSavedNotification>,
1112
IDistributedCacheNotificationHandler<ContentMovedNotification>,
1213
IDistributedCacheNotificationHandler<ContentMovedToRecycleBinNotification>,
1314
IDistributedCacheNotificationHandler<ContentDeletedNotification>
1415
{
15-
private readonly DistributedCache _distributedCache;
16+
protected override Guid CacheRefresherUniqueId => DraftContentCacheRefresher.UniqueId;
1617

17-
public DraftContentNotificationHandler(DistributedCache distributedCache)
18-
=> _distributedCache = distributedCache;
18+
public DraftContentNotificationHandler(
19+
DistributedCache distributedCache,
20+
IOriginProvider originProvider,
21+
IIndexDocumentService indexDocumentService)
22+
: base(distributedCache, originProvider, indexDocumentService)
23+
{
24+
}
1925

2026
public void Handle(ContentSavedNotification notification)
2127
{
22-
DraftContentCacheRefresher.JsonPayload[] payloads = notification
23-
.SavedEntities
28+
IContent[] savedEntities = notification.SavedEntities.ToArray();
29+
if (savedEntities.Length is 0)
30+
{
31+
return;
32+
}
33+
34+
FlushDocumentIndexCache(savedEntities);
35+
36+
DraftContentCacheRefresher.JsonPayload[] payloads = savedEntities
2437
.Select(entity => new DraftContentCacheRefresher.JsonPayload(entity.Key, TreeChangeTypes.RefreshNode))
2538
.ToArray();
2639

27-
_distributedCache.RefreshByPayload(DraftContentCacheRefresher.UniqueId, payloads);
40+
HandlePayloads(payloads);
2841
}
2942

3043
public void Handle(ContentMovedNotification notification)
@@ -35,21 +48,39 @@ public void Handle(ContentMovedToRecycleBinNotification notification)
3548

3649
public void Handle(ContentDeletedNotification notification)
3750
{
38-
DraftContentCacheRefresher.JsonPayload[] payloads = notification
39-
.DeletedEntities
51+
IContent[] deletedEntities = notification.DeletedEntities.ToArray();
52+
if (deletedEntities.Length is 0)
53+
{
54+
return;
55+
}
56+
57+
FlushDocumentIndexCache(deletedEntities);
58+
59+
DraftContentCacheRefresher.JsonPayload[] payloads = deletedEntities
4060
.Select(entity => new DraftContentCacheRefresher.JsonPayload(entity.Key, TreeChangeTypes.Remove))
4161
.ToArray();
4262

43-
_distributedCache.RefreshByPayload(DraftContentCacheRefresher.UniqueId, payloads);
63+
HandlePayloads(payloads);
4464
}
4565

4666
private void HandleMove(IEnumerable<MoveEventInfoBase<IContent>> moveEventInfo)
4767
{
48-
IContent[] topmostEntities = FindTopmostEntities(moveEventInfo.Select(i => i.Entity));
68+
IContent[] movedEntities = moveEventInfo.Select(i => i.Entity).ToArray();
69+
if (movedEntities.Length is 0)
70+
{
71+
return;
72+
}
73+
74+
FlushDocumentIndexCache(movedEntities);
75+
76+
IContent[] topmostEntities = FindTopmostEntities(movedEntities);
4977
DraftContentCacheRefresher.JsonPayload[] payloads = topmostEntities
5078
.Select(entity => new DraftContentCacheRefresher.JsonPayload(entity.Key, TreeChangeTypes.RefreshBranch))
5179
.ToArray();
5280

53-
_distributedCache.RefreshByPayload(DraftContentCacheRefresher.UniqueId, payloads);
81+
HandlePayloads(payloads);
5482
}
83+
84+
private void FlushDocumentIndexCache(IEnumerable<IContent> entities)
85+
=> FlushDocumentIndexCache(entities.Select(x => x.Key).ToArray(), false);
5586
}

src/Umbraco.Cms.Search.Core/Cache/Content/PublishedContentCacheRefresher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace Umbraco.Cms.Search.Core.Cache.Content;
77

8-
internal sealed class PublishedContentCacheRefresher : PayloadCacheRefresherBase<PublishedContentCacheRefresherNotification, PublishedContentCacheRefresher.JsonPayload>
8+
internal sealed class PublishedContentCacheRefresher : PayloadCacheRefresherBase<PublishedContentCacheRefresherNotification, ContentCacheRefresherNotificationPayload<PublishedContentCacheRefresher.JsonPayload>>
99
{
1010
public static readonly Guid UniqueId = Guid.Parse("6BDC4BA1-5454-436B-80AC-FD13442CD216");
1111

src/Umbraco.Cms.Search.Core/Cache/Content/PublishedContentNotificationHandler.cs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,38 @@
33
using Umbraco.Cms.Core.Models;
44
using Umbraco.Cms.Core.Notifications;
55
using Umbraco.Cms.Core.Services.Changes;
6+
using Umbraco.Cms.Search.Core.Services.ContentIndexing;
67
using Umbraco.Extensions;
78

89
namespace Umbraco.Cms.Search.Core.Cache.Content;
910

10-
internal sealed class PublishedContentNotificationHandler : ContentNotificationHandlerBase,
11+
internal sealed class PublishedContentNotificationHandler : ContentNotificationHandlerBase<PublishedContentCacheRefresher.JsonPayload>,
1112
IDistributedCacheNotificationHandler<ContentPublishedNotification>,
1213
IDistributedCacheNotificationHandler<ContentUnpublishedNotification>,
1314
IDistributedCacheNotificationHandler<ContentMovedNotification>,
1415
INotificationHandler<ContentMovedToRecycleBinNotification>
1516
{
16-
private readonly DistributedCache _distributedCache;
17+
protected override Guid CacheRefresherUniqueId => PublishedContentCacheRefresher.UniqueId;
1718

18-
public PublishedContentNotificationHandler(DistributedCache distributedCache)
19-
=> _distributedCache = distributedCache;
19+
public PublishedContentNotificationHandler(
20+
DistributedCache distributedCache,
21+
IOriginProvider originProvider,
22+
IIndexDocumentService indexDocumentService)
23+
: base(distributedCache, originProvider, indexDocumentService)
24+
{
25+
}
2026

2127
public void Handle(ContentPublishedNotification notification)
2228
{
2329
// we sometimes get unpublished entities here... filter those out, we don't need them
2430
IContent[] publishedEntities = notification.PublishedEntities.Where(entity => entity.Published).ToArray();
25-
if (publishedEntities.Any() is false)
31+
if (publishedEntities.Length is 0)
2632
{
2733
return;
2834
}
2935

36+
FlushDocumentIndexCache(publishedEntities);
37+
3038
IContent[] topmostEntities = FindTopmostEntities(publishedEntities);
3139
PublishedContentCacheRefresher.JsonPayload[] payloads = topmostEntities
3240
.Select(entity =>
@@ -45,17 +53,24 @@ public void Handle(ContentPublishedNotification notification)
4553
.WhereNotNull()
4654
.ToArray();
4755

48-
_distributedCache.RefreshByPayload(PublishedContentCacheRefresher.UniqueId, payloads);
56+
HandlePayloads(payloads);
4957
}
5058

5159
public void Handle(ContentUnpublishedNotification notification)
5260
{
53-
PublishedContentCacheRefresher.JsonPayload[] payloads = notification
54-
.UnpublishedEntities
61+
IContent[] unpublishedEntities = notification.UnpublishedEntities.ToArray();
62+
if (unpublishedEntities.Length is 0)
63+
{
64+
return;
65+
}
66+
67+
FlushDocumentIndexCache(unpublishedEntities);
68+
69+
PublishedContentCacheRefresher.JsonPayload[] payloads = unpublishedEntities
5570
.Select(entity => new PublishedContentCacheRefresher.JsonPayload(entity.Key, TreeChangeTypes.Remove, []))
5671
.ToArray();
5772

58-
_distributedCache.RefreshByPayload(PublishedContentCacheRefresher.UniqueId, payloads);
73+
HandlePayloads(payloads);
5974
}
6075

6176
public void Handle(ContentMovedNotification notification)
@@ -66,11 +81,22 @@ public void Handle(ContentMovedToRecycleBinNotification notification)
6681

6782
private void HandleMove(IEnumerable<MoveEventInfoBase<IContent>> moveEventInfo, TreeChangeTypes changeType)
6883
{
69-
IContent[] topmostEntities = FindTopmostEntities(moveEventInfo.Select(i => i.Entity));
84+
IContent[] movedEntities = moveEventInfo.Select(i => i.Entity).ToArray();
85+
if (movedEntities.Length is 0)
86+
{
87+
return;
88+
}
89+
90+
FlushDocumentIndexCache(movedEntities);
91+
92+
IContent[] topmostEntities = FindTopmostEntities(movedEntities);
7093
PublishedContentCacheRefresher.JsonPayload[] payloads = topmostEntities
7194
.Select(entity => new PublishedContentCacheRefresher.JsonPayload(entity.Key, changeType, []))
7295
.ToArray();
7396

74-
_distributedCache.RefreshByPayload(PublishedContentCacheRefresher.UniqueId, payloads);
97+
HandlePayloads(payloads);
7598
}
99+
100+
private void FlushDocumentIndexCache(IEnumerable<IContent> entities)
101+
=> FlushDocumentIndexCache(entities.Select(x => x.Key).ToArray(), true);
76102
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Umbraco.Cms.Search.Core.Cache;
2+
3+
internal record ContentCacheRefresherNotificationPayload<TPayload>(TPayload[] Payloads, string Origin)
4+
{
5+
}
Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,51 @@
1+
using Umbraco.Cms.Core.Cache;
12
using Umbraco.Cms.Core.Models;
3+
using Umbraco.Cms.Core.Services.Changes;
4+
using Umbraco.Cms.Search.Core.Extensions;
5+
using Umbraco.Cms.Search.Core.Services.ContentIndexing;
26

37
namespace Umbraco.Cms.Search.Core.Cache;
48

5-
internal abstract class ContentNotificationHandlerBase
9+
internal abstract class ContentNotificationHandlerBase<TPayload>
610
{
11+
private readonly DistributedCache _distributedCache;
12+
private readonly IOriginProvider _originProvider;
13+
private readonly IIndexDocumentService _indexDocumentService;
14+
15+
protected ContentNotificationHandlerBase(DistributedCache distributedCache, IOriginProvider originProvider, IIndexDocumentService indexDocumentService)
16+
{
17+
_distributedCache = distributedCache;
18+
_originProvider = originProvider;
19+
_indexDocumentService = indexDocumentService;
20+
}
21+
22+
protected abstract Guid CacheRefresherUniqueId { get; }
23+
724
protected T[] FindTopmostEntities<T>(IEnumerable<T> candidates)
825
where T : IContentBase
926
{
1027
T[] candidatesAsArray = candidates as T[] ?? candidates.ToArray();
1128
var ids = candidatesAsArray.Select(entity => entity.Id).ToArray();
1229
return candidatesAsArray.Where(entity => ids.Contains(entity.ParentId) is false).ToArray();
1330
}
31+
32+
protected void HandlePayloads(TPayload[] payloads)
33+
{
34+
var payload = new ContentCacheRefresherNotificationPayload<TPayload>(payloads, _originProvider.GetCurrent());
35+
_distributedCache.RefreshByPayload(CacheRefresherUniqueId, [payload]);
36+
}
37+
38+
protected void FlushDocumentIndexCache(Guid[] ids, bool published)
39+
=> _indexDocumentService.DeleteAsync(ids, published).GetAwaiter().GetResult();
40+
41+
protected void ClearDocumentIndexCache()
42+
=> _indexDocumentService.DeleteAllAsync().GetAwaiter().GetResult();
43+
44+
protected void ClearDocumentIndexCacheForStructuralChanges(IEnumerable<ContentTypeChangeTypes> changes)
45+
{
46+
if (changes.Any(change => change.RequiresIndexRebuild()))
47+
{
48+
ClearDocumentIndexCache();
49+
}
50+
}
1451
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Umbraco.Cms.Core.Cache;
2+
using Umbraco.Cms.Core.Events;
3+
using Umbraco.Cms.Core.Serialization;
4+
using Umbraco.Cms.Core.Services.Changes;
5+
6+
namespace Umbraco.Cms.Search.Core.Cache.ContentType;
7+
8+
internal sealed class ContentTypeCacheRefresher : PayloadCacheRefresherBase<ContentTypeCacheRefresherNotification, ContentCacheRefresherNotificationPayload<ContentTypeCacheRefresher.JsonPayload>>
9+
{
10+
public static readonly Guid UniqueId = Guid.Parse("9EC8AAAB-FEBA-4F58-819B-5B1C6E80F988");
11+
12+
public ContentTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
13+
: base(appCaches, serializer, eventAggregator, factory)
14+
{
15+
}
16+
17+
public override Guid RefresherUniqueId => UniqueId;
18+
19+
public override string Name => "Content Type Cache Refresher";
20+
21+
public record JsonPayload(Guid ContentTypeKey, ContentTypeChangeTypes ChangeTypes)
22+
{
23+
}
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Umbraco.Cms.Core.Notifications;
2+
using Umbraco.Cms.Core.Sync;
3+
4+
namespace Umbraco.Cms.Search.Core.Cache.ContentType;
5+
6+
internal sealed class ContentTypeCacheRefresherNotification : CacheRefresherNotification
7+
{
8+
public ContentTypeCacheRefresherNotification(object messageObject, MessageType messageType)
9+
: base(messageObject, messageType)
10+
{
11+
}
12+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using Umbraco.Cms.Core.Cache;
2+
using Umbraco.Cms.Core.Notifications;
3+
using Umbraco.Cms.Search.Core.Services.ContentIndexing;
4+
5+
namespace Umbraco.Cms.Search.Core.Cache.ContentType;
6+
7+
internal sealed class ContentTypeNotificationHandler
8+
: ContentNotificationHandlerBase<ContentTypeCacheRefresher.JsonPayload>,
9+
IDistributedCacheNotificationHandler<ContentTypeChangedNotification>
10+
{
11+
public ContentTypeNotificationHandler(
12+
DistributedCache distributedCache,
13+
IOriginProvider originProvider,
14+
IIndexDocumentService indexDocumentService)
15+
: base(distributedCache, originProvider, indexDocumentService)
16+
{
17+
}
18+
19+
protected override Guid CacheRefresherUniqueId => ContentTypeCacheRefresher.UniqueId;
20+
21+
public void Handle(ContentTypeChangedNotification notification)
22+
{
23+
ContentTypeCacheRefresher.JsonPayload[] payloads = notification
24+
.Changes
25+
.Select(change => new ContentTypeCacheRefresher.JsonPayload(change.Item.Key, change.ChangeTypes))
26+
.ToArray();
27+
28+
ClearDocumentIndexCacheForStructuralChanges(payloads.Select(payload => payload.ChangeTypes));
29+
30+
HandlePayloads(payloads);
31+
}
32+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Umbraco.Cms.Core.Cache;
2+
using Umbraco.Cms.Core.Events;
3+
using Umbraco.Cms.Core.Serialization;
4+
5+
namespace Umbraco.Cms.Search.Core.Cache.Language;
6+
7+
internal sealed class LanguageCacheRefresher : PayloadCacheRefresherBase<LanguageCacheRefresherNotification, ContentCacheRefresherNotificationPayload<LanguageCacheRefresher.JsonPayload>>
8+
{
9+
public static readonly Guid UniqueId = Guid.Parse("EB0208D6-9EC5-4B88-B2CE-62C0BFF1DB9A");
10+
11+
public LanguageCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
12+
: base(appCaches, serializer, eventAggregator, factory)
13+
{
14+
}
15+
16+
public override Guid RefresherUniqueId => UniqueId;
17+
18+
public override string Name => "Language Cache Refresher";
19+
20+
public record JsonPayload(Guid LanguageKey, string IsoCode, LanguageChangeTypes ChangeTypes)
21+
{
22+
}
23+
}

0 commit comments

Comments
 (0)