From 27726169f0a178c41edd3d9464734bfcd366714b Mon Sep 17 00:00:00 2001 From: Henrik Gedionsen Date: Mon, 20 Apr 2026 09:05:02 +0200 Subject: [PATCH 1/2] Avoid allocating a string if _publishedContentCache has a cached version & removed preview param, it was always false --- .../Services/MediaCacheService.cs | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs b/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs index e7304ba4fd2c..da36bb37fbb3 100644 --- a/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs +++ b/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs @@ -32,7 +32,7 @@ internal sealed class MediaCacheService : IMediaCacheService private readonly ILogger _logger; private readonly CacheSettings _cacheSettings; - private readonly ConcurrentDictionary _publishedContentCache = []; + private readonly ConcurrentDictionary _publishedContentCache = []; private HashSet? _seedKeys; private HashSet SeedKeys @@ -105,13 +105,12 @@ public MediaCacheService( private async Task GetNodeAsync(Guid key) { - var cacheKey = $"{key}"; - - if (_publishedContentCache.TryGetValue(cacheKey, out IPublishedContent? cached)) + if (_publishedContentCache.TryGetValue(key, out IPublishedContent? cached)) { return cached; } + string cacheKey = GetCacheKey(key); (bool exists, ContentCacheNode? contentCacheNode) = await _hybridCache.TryGetValueAsync(cacheKey, CancellationToken.None); if (exists is false) { @@ -135,7 +134,7 @@ await _hybridCache.SetAsync( IPublishedContent? result = _publishedContentFactory.ToIPublishedMedia(contentCacheNode).CreateModel(_publishedModelFactory); if (result is not null) { - _publishedContentCache[cacheKey] = result; + _publishedContentCache[key] = result; } return result; @@ -174,7 +173,7 @@ public async Task RefreshMediaAsync(IMedia media) var cacheNode = _cacheNodeFactory.ToContentCacheNode(media); await _databaseCacheRepository.RefreshMediaAsync(cacheNode); - _publishedContentCache.Remove(GetCacheKey(media.Key, false), out _); + _publishedContentCache.Remove(media.Key, out _); scope.Complete(); } @@ -202,9 +201,7 @@ public async Task SeedAsync(CancellationToken cancellationToken) break; } - var cacheKey = GetCacheKey(key, false); - - var existsInCache = await _hybridCache.ExistsAsync(cacheKey, CancellationToken.None); + var existsInCache = await _hybridCache.ExistsAsync(GetCacheKey(key), CancellationToken.None); if (existsInCache is false) { uncachedKeys.Add(key); @@ -228,9 +225,8 @@ public async Task SeedAsync(CancellationToken cancellationToken) foreach (ContentCacheNode cacheNode in cacheNodes) { - var cacheKey = GetCacheKey(cacheNode.Key, false); await _hybridCache.SetAsync( - cacheKey, + GetCacheKey(cacheNode.Key), cacheNode, GetSeedEntryOptions(), GenerateTags(cacheNode), @@ -253,9 +249,8 @@ public async Task RefreshMemoryCacheAsync(Guid key) ContentCacheNode? publishedNode = await _databaseCacheRepository.GetMediaSourceAsync(key); if (publishedNode is not null) { - var cacheKey = GetCacheKey(publishedNode.Key, false); - await _hybridCache.SetAsync(cacheKey, publishedNode, GetEntryOptions(publishedNode.Key)); - _publishedContentCache.Remove(cacheKey, out _); + await _hybridCache.SetAsync(GetCacheKey(publishedNode.Key), publishedNode, GetEntryOptions(publishedNode.Key)); + _publishedContentCache.Remove(key, out _); } else { @@ -335,7 +330,7 @@ private HybridCacheEntryOptions GetEntryOptions(Guid key) LocalCacheExpiration = _cacheSettings.Entry.Media.SeedCacheDuration, }; - private static string GetCacheKey(Guid key, bool preview) => preview ? $"{key}+draft" : $"{key}"; + private static string GetCacheKey(Guid key) => $"{key}"; // Generates the cache tags for a given CacheNode // We use the tags to be able to clear all cache entries that are related to a given content item. @@ -344,9 +339,8 @@ private HybridCacheEntryOptions GetEntryOptions(Guid key) private async Task ClearPublishedCacheAsync(Guid key) { - var cacheKey = GetCacheKey(key, false); - await _hybridCache.RemoveAsync(cacheKey); - _publishedContentCache.Remove(cacheKey, out _); + await _hybridCache.RemoveAsync(GetCacheKey(key)); + _publishedContentCache.Remove(key, out _); } private static string MediaTypeIdTag(int mediaTypeId) From 9cca8d432487b74b0b60118a1389c61e059e1d59 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 20 Apr 2026 11:08:43 +0200 Subject: [PATCH 2/2] Clarified comment, used GetCacheKey method from location where string was being created. --- .../Services/MediaCacheService.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs b/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs index da36bb37fbb3..dbfdbc7a4252 100644 --- a/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs +++ b/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs @@ -156,7 +156,7 @@ public async Task HasContentByIdAsync(int id) return false; } - return await _hybridCache.ExistsAsync($"{keyAttempt.Result}", CancellationToken.None); + return await _hybridCache.ExistsAsync(GetCacheKey(keyAttempt.Result), CancellationToken.None); } public async Task RefreshMediaAsync(IMedia media) @@ -332,9 +332,8 @@ private HybridCacheEntryOptions GetEntryOptions(Guid key) private static string GetCacheKey(Guid key) => $"{key}"; - // Generates the cache tags for a given CacheNode + // Generates the cache tags for a given CacheNode. // We use the tags to be able to clear all cache entries that are related to a given content item. - // Tags for now are only content/media, but can be expanded with draft/published later. private static HashSet GenerateTags(ContentCacheNode? cacheNode) => cacheNode is null ? [] : [Constants.Cache.Tags.Media, MediaTypeIdTag(cacheNode.ContentTypeId)]; private async Task ClearPublishedCacheAsync(Guid key)