Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V15: Refresh caches on load balanced environments #17296

Merged
merged 19 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
private readonly IDocumentNavigationQueryService _documentNavigationQueryService;
private readonly IDocumentNavigationManagementService _documentNavigationManagementService;
private readonly IContentService _contentService;
private readonly IDocumentCacheService _documentCacheService;
private readonly IPublishStatusManagementService _publishStatusManagementService;
private readonly IIdKeyMap _idKeyMap;

Expand All @@ -35,16 +36,18 @@
IDocumentNavigationQueryService documentNavigationQueryService,
IDocumentNavigationManagementService documentNavigationManagementService,
IContentService contentService,
IPublishStatusManagementService publishStatusManagementService)
IPublishStatusManagementService publishStatusManagementService,
IDocumentCacheService documentCacheService)
: base(appCaches, serializer, eventAggregator, factory)
{
_idKeyMap = idKeyMap;
_domainService = domainService;
_domainCacheService = domainCacheService;
_documentUrlService = documentUrlService;
_documentNavigationQueryService = documentNavigationQueryService;
_documentNavigationManagementService = documentNavigationManagementService;
_contentService = contentService;
_documentCacheService = documentCacheService;

Check notice on line 50 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (release/15.0)

ℹ Getting worse: Constructor Over-Injection

ContentCacheRefresher increases from 12 to 13 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.
_publishStatusManagementService = publishStatusManagementService;
}

Expand Down Expand Up @@ -107,6 +110,7 @@
}


HandleMemoryCache(payload);
HandleRouting(payload);

HandleNavigation(payload);
Expand Down Expand Up @@ -143,6 +147,45 @@
base.Refresh(payloads);
}

private void HandleMemoryCache(JsonPayload payload)
{
Guid key = payload.Key ?? _idKeyMap.GetKeyForId(payload.Id, UmbracoObjectTypes.Document).Result;

if (payload.Blueprint)
{
return;
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshNode))
{
_documentCacheService.RefreshMemoryCacheAsync(key).GetAwaiter().GetResult();
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch))
{
if (_documentNavigationQueryService.TryGetDescendantsKeys(key, out IEnumerable<Guid> descendantsKeys))
{
var branchKeys = descendantsKeys.ToList();
branchKeys.Add(key);

foreach (Guid branchKey in branchKeys)
{
_documentCacheService.RefreshMemoryCacheAsync(branchKey).GetAwaiter().GetResult();
}
}
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))
{
_documentCacheService.ClearMemoryCacheAsync(CancellationToken.None).GetAwaiter().GetResult();
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove))
{
_documentCacheService.RemoveFromMemoryCacheAsync(key).GetAwaiter().GetResult();
}
}

Check warning on line 187 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (release/15.0)

❌ New issue: Complex Method

HandleMemoryCache has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

private void HandleNavigation(JsonPayload payload)
{

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Changes;
Expand All @@ -15,6 +16,9 @@
private readonly IContentTypeCommonRepository _contentTypeCommonRepository;
private readonly IPublishedModelFactory _publishedModelFactory;
private readonly IPublishedContentTypeFactory _publishedContentTypeFactory;
private readonly IDocumentCacheService _documentCacheService;
private readonly IPublishedContentTypeCache _publishedContentTypeCache;
private readonly IMediaCacheService _mediaCacheService;
private readonly IIdKeyMap _idKeyMap;

public ContentTypeCacheRefresher(
Expand All @@ -25,13 +29,19 @@
IEventAggregator eventAggregator,
ICacheRefresherNotificationFactory factory,
IPublishedModelFactory publishedModelFactory,
IPublishedContentTypeFactory publishedContentTypeFactory)
IPublishedContentTypeFactory publishedContentTypeFactory,
IDocumentCacheService documentCacheService,
IPublishedContentTypeCache publishedContentTypeCache,
IMediaCacheService mediaCacheService)
: base(appCaches, serializer, eventAggregator, factory)
{
_idKeyMap = idKeyMap;
_contentTypeCommonRepository = contentTypeCommonRepository;
_publishedModelFactory = publishedModelFactory;
_publishedContentTypeFactory = publishedContentTypeFactory;
_documentCacheService = documentCacheService;
_publishedContentTypeCache = publishedContentTypeCache;
_mediaCacheService = mediaCacheService;

Check notice on line 44 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentTypeCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (release/15.0)

ℹ Getting worse: Constructor Over-Injection

ContentTypeCacheRefresher increases from 8 to 11 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.
}

#region Json
Expand Down Expand Up @@ -114,10 +124,16 @@
MemberCacheRefresher.RefreshMemberTypes(AppCaches);
}

// TODO: We need to clear the HybridCache of any content using the ContentType, but NOT the database cache here, and this should be done within the "WithSafeLiveFactoryReset" to ensure that the factory is locked in the meantime.
_publishedModelFactory.WithSafeLiveFactoryReset(() => { });

_publishedContentTypeCache.ClearContentTypes(payloads.Select(x => x.Id));
_publishedContentTypeFactory.NotifyDataTypeChanges();
_publishedModelFactory.WithSafeLiveFactoryReset(() =>
{
IEnumerable<int> documentTypeIds = payloads.Where(x => x.ItemType == nameof(IContentType)).Select(x => x.Id);
IEnumerable<int> mediaTypeIds = payloads.Where(x => x.ItemType == nameof(IMediaType)).Select(x => x.Id);

_documentCacheService.RebuildMemoryCacheByContentTypeAsync(documentTypeIds);
_mediaCacheService.RebuildMemoryCacheByContentTypeAsync(mediaTypeIds);
});

// now we can trigger the event
base.Refresh(payloads);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;
Expand All @@ -14,6 +15,9 @@
private readonly IIdKeyMap _idKeyMap;
private readonly IPublishedModelFactory _publishedModelFactory;
private readonly IPublishedContentTypeFactory _publishedContentTypeFactory;
private readonly IPublishedContentTypeCache _publishedContentTypeCache;
private readonly IDocumentCacheService _documentCacheService;
private readonly IMediaCacheService _mediaCacheService;

public DataTypeCacheRefresher(
AppCaches appCaches,
Expand All @@ -22,12 +26,18 @@
IEventAggregator eventAggregator,
ICacheRefresherNotificationFactory factory,
IPublishedModelFactory publishedModelFactory,
IPublishedContentTypeFactory publishedContentTypeFactory)
IPublishedContentTypeFactory publishedContentTypeFactory,
IPublishedContentTypeCache publishedContentTypeCache,
IDocumentCacheService documentCacheService,
IMediaCacheService mediaCacheService)
: base(appCaches, serializer, eventAggregator, factory)
{
_idKeyMap = idKeyMap;
_publishedModelFactory = publishedModelFactory;
_publishedContentTypeFactory = publishedContentTypeFactory;
_publishedContentTypeCache = publishedContentTypeCache;
_documentCacheService = documentCacheService;
_mediaCacheService = mediaCacheService;

Check notice on line 40 in src/Umbraco.Core/Cache/Refreshers/Implement/DataTypeCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (release/15.0)

ℹ Getting worse: Constructor Over-Injection

DataTypeCacheRefresher increases from 7 to 10 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.
}

#region Json
Expand Down Expand Up @@ -76,6 +86,7 @@

Attempt<IAppPolicyCache?> dataTypeCache = AppCaches.IsolatedCaches.Get<IDataType>();

List<IPublishedContentType> removedContentTypes = new();
foreach (JsonPayload payload in payloads)
{
_idKeyMap.ClearCache(payload.Id);
Expand All @@ -84,14 +95,25 @@
{
dataTypeCache.Result?.Clear(RepositoryCacheKeys.GetKey<IDataType, int>(payload.Id));
}
}

// TODO: We need to clear the HybridCache of any content using the ContentType, but NOT the database cache here, and this should be done within the "WithSafeLiveFactoryReset" to ensure that the factory is locked in the meantime.
_publishedModelFactory.WithSafeLiveFactoryReset(() => { });
removedContentTypes.AddRange(_publishedContentTypeCache.ClearByDataTypeId(payload.Id));
}

var changedIds = payloads.Select(x => x.Id).ToArray();
_publishedContentTypeFactory.NotifyDataTypeChanges(changedIds);

_publishedModelFactory.WithSafeLiveFactoryReset(() =>
{
IEnumerable<int> documentTypeIds = removedContentTypes
.Where(x => x.ItemType == PublishedItemType.Content)
.Select(x => x.Id);
_documentCacheService.RebuildMemoryCacheByContentTypeAsync(documentTypeIds).GetAwaiter().GetResult();

IEnumerable<int> mediaTypeIds = removedContentTypes
.Where(x => x.ItemType == PublishedItemType.Media)
.Select(x => x.Id);
_mediaCacheService.RebuildMemoryCacheByContentTypeAsync(mediaTypeIds);
});
base.Refresh(payloads);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Changes;
Expand All @@ -16,6 +17,7 @@
private readonly IMediaNavigationQueryService _mediaNavigationQueryService;
private readonly IMediaNavigationManagementService _mediaNavigationManagementService;
private readonly IMediaService _mediaService;
private readonly IMediaCacheService _mediaCacheService;

public MediaCacheRefresher(
AppCaches appCaches,
Expand All @@ -25,13 +27,15 @@
ICacheRefresherNotificationFactory factory,
IMediaNavigationQueryService mediaNavigationQueryService,
IMediaNavigationManagementService mediaNavigationManagementService,
IMediaService mediaService)
IMediaService mediaService,
IMediaCacheService mediaCacheService)
: base(appCaches, serializer, eventAggregator, factory)
{
_idKeyMap = idKeyMap;
_mediaNavigationQueryService = mediaNavigationQueryService;
_mediaNavigationManagementService = mediaNavigationManagementService;
_mediaService = mediaService;
_mediaCacheService = mediaCacheService;

Check notice on line 38 in src/Umbraco.Core/Cache/Refreshers/Implement/MediaCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (release/15.0)

ℹ Getting worse: Constructor Over-Injection

MediaCacheRefresher increases from 8 to 9 arguments, threshold = 5. This constructor has too many arguments, indicating an object with low cohesion or missing function argument abstraction. Avoid adding more arguments.
}

#region Indirect
Expand Down Expand Up @@ -106,6 +110,7 @@
}
}

HandleMemoryCache(payload);
HandleNavigation(payload);
}

Expand All @@ -115,6 +120,41 @@
base.Refresh(payloads);
}

private void HandleMemoryCache(JsonPayload payload)
{
Guid key = payload.Key ?? _idKeyMap.GetKeyForId(payload.Id, UmbracoObjectTypes.Document).Result;


if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshNode))
{
_mediaCacheService.RefreshMemoryCacheAsync(key).GetAwaiter().GetResult();
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch))
{
if (_mediaNavigationQueryService.TryGetDescendantsKeys(key, out IEnumerable<Guid> descendantsKeys))
{
var branchKeys = descendantsKeys.ToList();
branchKeys.Add(key);

foreach (Guid branchKey in branchKeys)
{
_mediaCacheService.RefreshMemoryCacheAsync(branchKey).GetAwaiter().GetResult();
}
}
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))
{
_mediaCacheService.ClearMemoryCacheAsync(CancellationToken.None).GetAwaiter().GetResult();
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove))
{
_mediaCacheService.RemoveFromMemoryCacheAsync(key).GetAwaiter().GetResult();
}
}

private void HandleNavigation(JsonPayload payload)
{
if (payload.Key is null)
Expand Down
3 changes: 3 additions & 0 deletions src/Umbraco.Core/Configuration/Models/NuCacheSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class NuCacheSettings
/// <summary>
/// Gets or sets a value defining the BTree block size.
/// </summary>
[Obsolete("This property is no longer used")]
public int? BTreeBlockSize { get; set; }

/// <summary>
Expand All @@ -37,8 +38,10 @@ public class NuCacheSettings
/// The size to use for nucache Kit batches. Higher value means more content loaded into memory at a time.
/// </summary>
[DefaultValue(StaticKitBatchSize)]
[Obsolete("This property is no longer used")]
public int KitBatchSize { get; set; } = StaticKitBatchSize;

[Obsolete("This property is no longer used")]
public bool UnPublishedContentCompression { get; set; } = false;

[DefaultValue(StaticUsePagedSqlQuery)]
Expand Down
8 changes: 8 additions & 0 deletions src/Umbraco.Core/Constants-Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public static class Configuration
public const string ConfigPackageManifests = ConfigPrefix + "PackageManifests";
public const string ConfigWebhook = ConfigPrefix + "Webhook";
public const string ConfigCache = ConfigPrefix + "Cache";
public const string ConfigCacheEntry = ConfigCache + ":Entry";

public static class NamedOptions
{
Expand All @@ -79,6 +80,13 @@ public static class InstallDefaultData

public const string MemberTypes = "MemberTypes";
}

public static class CacheEntry
{
public const string Document = "Document";

public const string Media = "Media";
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@
builder.Services.Configure<InstallDefaultDataSettings>(
Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes,
builder.Config.GetSection($"{Constants.Configuration.ConfigInstallDefaultData}:{Constants.Configuration.NamedOptions.InstallDefaultData.MemberTypes}"));
builder.Services.Configure<CacheEntrySettings>(Constants.Configuration.NamedOptions.CacheEntry.Media,
builder.Config.GetSection($"{Constants.Configuration.ConfigCacheEntry}:{Constants.Configuration.NamedOptions.CacheEntry.Media}"));
builder.Services.Configure<CacheEntrySettings>(Constants.Configuration.NamedOptions.CacheEntry.Document,
builder.Config.GetSection($"{Constants.Configuration.ConfigCacheEntry}:{Constants.Configuration.NamedOptions.CacheEntry.Document}"));

Check warning on line 110 in src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (release/15.0)

❌ Getting worse: Large Method

AddConfiguration increases from 72 to 76 lines of code, threshold = 70. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.

// TODO: Remove this in V12
// This is to make the move of the AllowEditInvariantFromNonDefault setting from SecuritySettings to ContentSettings backwards compatible
Expand Down
19 changes: 19 additions & 0 deletions src/Umbraco.Core/Models/CacheEntrySettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.ComponentModel;

namespace Umbraco.Cms.Core.Models;

public class CacheEntrySettings
{
internal const string StaticLocalCacheDuration = "1.00:00:00";
internal const string StaticRemoteCacheDuration = "365.00:00:00";
internal const string StaticSeedCacheDuration = "365.00:00:00";

[DefaultValue(StaticLocalCacheDuration)]
public TimeSpan LocalCacheDuration { get; set; } = TimeSpan.Parse(StaticLocalCacheDuration);

[DefaultValue(StaticRemoteCacheDuration)]
public TimeSpan RemoteCacheDuration { get; set; } = TimeSpan.Parse(StaticRemoteCacheDuration);

[DefaultValue(StaticSeedCacheDuration)]
public TimeSpan SeedCacheDuration { get; set; } = TimeSpan.Parse(StaticSeedCacheDuration);
}
3 changes: 1 addition & 2 deletions src/Umbraco.Core/Models/CacheSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace Umbraco.Cms.Core.Models;
public class CacheSettings
{
internal const int StaticDocumentBreadthFirstSeedCount = 100;

internal const int StaticMediaBreadthFirstSeedCount = 100;
internal const string StaticSeedCacheDuration = "365.00:00:00";

Expand All @@ -20,10 +19,10 @@ public class CacheSettings
[DefaultValue(StaticDocumentBreadthFirstSeedCount)]
public int DocumentBreadthFirstSeedCount { get; set; } = StaticDocumentBreadthFirstSeedCount;


[DefaultValue(StaticMediaBreadthFirstSeedCount)]
public int MediaBreadthFirstSeedCount { get; set; } = StaticDocumentBreadthFirstSeedCount;

[Obsolete("Use Cache:Entry:Document:SeedCacheDuration instead")]
[DefaultValue(StaticSeedCacheDuration)]
public TimeSpan SeedCacheDuration { get; set; } = TimeSpan.Parse(StaticSeedCacheDuration);
}
Loading