Skip to content

Hybrid Cache: Element cache#22369

Merged
lauraneto merged 34 commits into
v18/devfrom
v18/feature/element-cache
Apr 20, 2026
Merged

Hybrid Cache: Element cache#22369
lauraneto merged 34 commits into
v18/devfrom
v18/feature/element-cache

Conversation

@lauraneto

@lauraneto lauraneto commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fully implements the element hybrid cache — the elements equivalent of the existing document and media hybrid caches. Elements now get the same multi-level caching (L0 converted element cache, L1/L2 HybridCache, database cache table) with notification-based invalidation.

Element cache service

  • ElementCacheService with full HybridCache backing + cmsContentNu database persistence
  • Draft/published separation, preview service support, cache seeding infrastructure
  • IContentCacheService base interface extracted and shared across document, media, and element cache services
  • IPublishedElementCache public facade with async-only API, exposed via ICacheManager.Elements and IUmbracoContext.Elements

Database layer

  • Element CRUD methods on IDatabaseCacheRepository (GetElementSource*, RefreshElementAsync, RebuildElementDbCache)
  • SqlElementSourcesSelect query using ElementDto/ElementVersionDto
  • ContentCacheDataSerializerEntityType.Element for serialization
  • Renamed document-specific methods for clarity (GetContentSourceGetDocumentSource, etc.)
  • Extracted shared RebuildPublishableDbCache to eliminate duplication between document and element rebuild

Cache invalidation

  • ElementRefreshNotification fired from ElementRepository.OnUowRefreshedEntity
  • CacheRefreshingNotificationHandler handles element notifications, routes content type changes by IsElement
  • Refactored handler to single-pass classification with RefreshCacheForContentTypeChanges

Navigation and seeding

  • ElementNavigationService with IElementNavigationQueryService/IElementNavigationManagementService
  • Queries both Element and ElementContainer object types to build full tree with containers
  • Multi-objectType overloads on INavigationRepository with LEFT JOIN for container support
  • ElementBreadthFirstKeyProvider traverses containers without seeding them, only counting published elements
  • ElementContentTypeSeedKeyProvider seeds elements by configured content type keys
  • Configurable ElementBreadthFirstSeedCount and ElementSeedBatchSize in CacheSettings

Publish status

  • Split PublishStatusService into abstract base + DocumentPublishStatusService / ElementPublishStatusService
  • Separate interfaces: IDocumentPublishStatusQueryService, IElementPublishStatusQueryService, etc.
  • Element publish status initialized on startup and kept in sync via ElementCacheRefresher
  • Non-breaking constructor changes with obsolete overloads for backward compatibility

Bug fix

  • Fixed CacheNodeFactory.ToContentCacheNode(IElement) hardcoding IsDraft = false instead of using the preview parameter — this prevented draft cmsContentNu rows from being written

Tests

  • CacheRefreshingNotificationHandlerTests — 8 tests including element type routing
  • ElementCacheServiceTests — 9 integration tests (rebuild, delete, refresh)
  • ElementHybridCacheTests — 7 integration tests via IPublishedElementCache
  • ElementHybridCacheElementTypeTests — 3 integration tests for type change invalidation
  • ElementBreadthFirstKeyProviderTests — 9 unit tests including container traversal

Breaking changes

  • PublishStatusService is now an abstract base class. Use DocumentPublishStatusService or ElementPublishStatusService instead of instantiating it directly.
  • IPublishStatusQueryService is now obsolete — use IDocumentPublishStatusQueryService or IElementPublishStatusQueryService directly.
  • IPublishStatusManagementService is now obsolete — use IDocumentPublishStatusManagementService or IElementPublishStatusManagementService directly.
  • ICacheManager has a new Elements property (IPublishedElementCache). Implementors must add this property.
  • IUmbracoContext has a new Elements property (IPublishedElementCache). Implementors must add this property.
  • CacheManager constructor now requires an IPublishedElementCache parameter.
  • INavigationRepository has new multi-objectType overloads: GetContentNodesByObjectType(IEnumerable<Guid>) and GetTrashedContentNodesByObjectType(IEnumerable<Guid>). The single-type overloads now delegate to these.
  • IDatabaseCacheRepository.Rebuild signature changed — added optional elementTypeIds parameter.
  • IDocumentCacheService, IMediaCacheService, IElementCacheService now extend IContentCacheService (additive, but implementors of these interfaces now implicitly implement IContentCacheService).
  • IElementCacheService interface expanded with new methods: RefreshElementAsync, DeleteItemAsync, Rebuild, RebuildMemoryCacheByContentTypeAsync, ClearConvertedContentCache, GetByContentType, SeedAsync.
  • Renamed repository methods: GetContentSourceAsyncGetDocumentSourceAsync, RefreshContentAsyncRefreshDocumentAsync, etc. (internal interface, but affects custom IDatabaseCacheRepository implementations).
  • ContentNavigationRepository single-type query now uses LEFT JOIN instead of INNER JOIN (behavioral change — may return nodes without umbracoContent rows).

Known issues

Warning

Trashing an item does not clear it from the published cache. A cached element (or document) remains accessible via the cache after being moved to the recycle bin. This is a pre-existing issue that also affects documents — it is not introduced by this PR. The Cannot_Get_Published_Again_After_Trashing test has been skipped pending a fix.

lauraneto added 13 commits April 2, 2026 10:33
…ache support

Fully implements ElementCacheService as the elements equivalent of DocumentCacheService,
backed by Microsoft HybridCache (L1 in-memory + L2 distributed) with database cache table
persistence via cmsContentNu.

Key changes:
- ElementCacheService: full implementation with HybridCache, draft/published separation,
  converted element L0 cache, cache tagging, preview service support, and seeding infrastructure
- IDatabaseCacheRepository: added element CRUD methods (Get/Refresh/Rebuild) with SQL queries
  using ElementDto/ElementVersionDto
- IContentCacheService: extracted common base interface shared by Document, Media and Element
  cache services (8 shared methods including Seed, Rebuild, memory cache operations)
- CacheRefreshingNotificationHandler: added element notification handling, content type changes
  now route to element or document service based on IsElement, refactored to single-pass
  classification with shared RefreshCacheForContentTypeChanges method
- ElementRefreshNotification: new notification wired to ElementRepository.OnUowRefreshedEntity
- Renamed document-specific methods for clarity (GetContentSource -> GetDocumentSource,
  RefreshContent -> RefreshDocument, CreateContentNodeKit -> CreateDocumentNodeKit,
  RebuildContentDbCache -> RebuildDocumentDbCache)
- Renamed shared DTOs (CacheRebuildDocumentDto -> CacheRebuildPublishableContentDto) since
  they're used by both documents and elements
- Extracted shared RebuildPublishableDbCache method to eliminate duplication between document
  and element rebuild logic
…first seeding

Adds the infrastructure needed for element cache seeding:

- ElementNavigationService: provides tree traversal for elements, following the
  same pattern as DocumentNavigationService/MediaNavigationService
- Split PublishStatusService into an abstract base class with DocumentPublishStatusService
  and ElementPublishStatusService subclasses, each with their own interfaces
  (IDocumentPublishStatusQueryService, IElementPublishStatusQueryService, etc.)
- ElementBreadthFirstKeyProvider: seeds the element cache on startup by traversing
  the element tree breadth-first, filtering out unpublished elements
- Element publish status is initialized on startup and kept in sync via
  ElementCacheRefresher
- Old IPublishStatusQueryService/IPublishStatusManagementService interfaces kept
  as obsolete for backward compatibility
- Non-breaking constructor changes for ContentCacheRefresher, DocumentUrlService,
  ApiContentRouteBuilder via obsolete constructor overloads
CacheNodeFactory.ToContentCacheNode(IElement, bool preview) was hardcoding
IsDraft = false instead of using the preview parameter. This caused
RefreshElementAsync to never write draft cmsContentNu rows, because
DatabaseCacheRepository.RefreshElementAsync skipped the draft write when
IsDraft was false.
RefreshMemoryCacheAsync was using Constants.Locks.ContentTree instead of
Constants.Locks.ElementTree for the read lock.
…stract base

- ElementCacheServiceTests: 9 integration tests covering draft/published retrieval,
  rebuild, delete, and RefreshElementAsync behavior
- Updated PublishStatusServiceTests to use DocumentPublishStatusService instead of
  the now-abstract PublishStatusService
Introduces the public-facing element cache interface and implementation,
following the same pattern as IPublishedContentCache/IPublishedMediaCache.

- IPublishedElementCache: async-only interface (no legacy sync methods)
- ElementCache: facade delegating to IElementCacheService
- Added Elements property to ICacheManager, IUmbracoContext, and their
  implementations
Integration tests exercising the full element cache pipeline via
IPublishedElementCache:

ElementHybridCacheTests (7 tests):
- Draft/published retrieval by key
- Unpublished element not accessible without preview
- Draft of published element accessible
- Updated draft element reflects changes
- Deleted element removed from cache
- Element name accessible

ElementHybridCacheElementTypeTests (3 tests):
- Structural type change removes property from cached element
- Non-structural type change preserves property values
- Element removed from cache when element type is deleted
…t seeding

The element tree contains both elements and containers (folders) with different
object types. The navigation service now queries both object types to build
the full tree hierarchy.

- Added multi-objectType overloads to INavigationRepository and
  ContentNavigationRepository using LEFT JOIN to support nodes without
  content rows (containers)
- Single-objectType methods now delegate to the multi-objectType implementation
- ElementNavigationService queries both Element and ElementContainer object types
- ElementBreadthFirstKeyProvider traverses containers without seeding them,
  only counting published elements toward the seed limit
- Added ElementBreadthFirstKeyProviderTests (9 tests) including container
  traversal scenarios
…seeding

Seeds elements whose content types match the configured CacheSettings.ContentTypeKeys,
mirroring the existing ContentTypeSeedKeyProvider for documents. Both providers read
from the same configuration list — document type keys seed documents, element type
keys seed elements.
…y overload

The single-type GetContentNodesByObjectType(Guid) now delegates to the
multi-type overload. Updated test mocks to match the IEnumerable<Guid>
signature, verifying exactly one key containing Constants.ObjectTypes.Document.
Trashing does not clear the published cache — this is a pre-existing issue
that also affects documents. When a cached item is trashed, the HybridCache
entry remains because RefreshMemoryCacheAsync does not remove entries when
the database returns null for trashed items.
@lauraneto lauraneto marked this pull request as ready for review April 9, 2026 08:27
Copilot AI review requested due to automatic review settings April 9, 2026 08:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the hybrid cache stack for elements (parity with document/media hybrid caches), including DB persistence, multi-level caching, cache seeding, and notification-based invalidation. It also refactors publish-status and navigation services to support elements alongside documents/media.

Changes:

  • Adds ElementCacheService + IPublishedElementCache facade, with seeding + invalidation wiring.
  • Extends HybridCache DB repository and rebuild pipeline to support element sources (cmsContentNu) and element-type rebuilds.
  • Splits publish-status and navigation services to handle documents vs elements cleanly, updating DI + consumers.

Reviewed changes

Copilot reviewed 72 out of 72 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.HybridCache/ElementBreadthFirstKeyProviderTests.cs Adds unit coverage for element seed-key breadth-first traversal incl. containers + publish filtering.
tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.HybridCache/CacheRefreshingNotificationHandlerTests.cs Extends handler tests to ensure element content-type changes route to the element cache service.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs Updates tests to the new document publish-status query interface.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/ContentNavigationServiceTest.cs Updates mocks for new multi-objectType navigation repository overload.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PropertyValueConverterTests.cs Updates Delivery API test wiring to use document publish-status query interface.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs Updates Delivery API tests to use document publish-status query interface.
tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/ElementHybridCacheTests.cs Adds integration coverage for fetching draft/published elements through IPublishedElementCache.
tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/ElementHybridCacheElementTypeTests.cs Adds integration coverage for cache invalidation behavior on element-type changes.
tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/ElementCacheServiceTests.cs Adds integration coverage for element DB cache rows, rebuild, refresh, delete behaviors.
tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheMockTests.cs Updates repository method names for document source retrieval.
tests/Umbraco.Tests.Integration/Umbraco.Core/Services/PublishStatusServiceTests.Management.cs Updates tests to instantiate DocumentPublishStatusService instead of the old concrete type.
tests/Umbraco.Tests.Integration/Umbraco.Core/Services/ElementPublishingServiceTests.Publish.cs Marks known pre-existing “trash doesn’t clear published cache” issue as ignored.
src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs Exposes Elements cache via IUmbracoContext.
src/Umbraco.PublishedCache.HybridCache/Services/ElementCacheService.cs Implements full element hybrid cache service (L0 converted cache + HybridCache + DB-backed nodes).
src/Umbraco.PublishedCache.HybridCache/Services/DocumentCacheService.cs Updates to renamed repository methods and new document publish-status query interface.
src/Umbraco.PublishedCache.HybridCache/Serialization/ContentCacheDataSerializerEntityType.cs Adds Element entity type flag for serialization.
src/Umbraco.PublishedCache.HybridCache/SeedKeyProviders/Element/ElementContentTypeSeedKeyProvider.cs Seeds element keys by configured content types, filtered by element publish status.
src/Umbraco.PublishedCache.HybridCache/SeedKeyProviders/Element/ElementBreadthFirstKeyProvider.cs Adds breadth-first element seeding that traverses containers but only seeds published elements.
src/Umbraco.PublishedCache.HybridCache/SeedKeyProviders/Document/DocumentBreadthFirstKeyProvider.cs Updates to the new document publish-status query interface.
src/Umbraco.PublishedCache.HybridCache/SeedKeyProviders/Document/ContentTypeSeedKeyProvider.cs Updates to the new document publish-status query interface.
src/Umbraco.PublishedCache.HybridCache/PublishedElement.cs Updates preview publish-status fallback to IDocumentPublishStatusQueryService.
src/Umbraco.PublishedCache.HybridCache/Persistence/IDatabaseCacheRepository.cs Adds element CRUD/source APIs and clarifies document-specific method names.
src/Umbraco.PublishedCache.HybridCache/Persistence/DatabaseCacheRepository.cs Implements element source queries + refresh + rebuild; refactors shared publishable rebuild logic.
src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/SeedingNotificationHandler.cs Adds element cache seeding at application start.
src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/CacheRefreshingNotificationHandler.cs Handles element refresh/delete notifications and routes content-type changes by IsElement.
src/Umbraco.PublishedCache.HybridCache/IMediaSeedKeyProvider.cs Converts empty interface body to a single-line interface declaration.
src/Umbraco.PublishedCache.HybridCache/IElementSeedKeyProvider.cs Introduces element-specific seed-key provider interface.
src/Umbraco.PublishedCache.HybridCache/IDocumentSeedKeyProvider.cs Converts empty interface body to a single-line interface declaration.
src/Umbraco.PublishedCache.HybridCache/Factories/CacheNodeFactory.cs Fixes element node draft flag to honor the preview parameter.
src/Umbraco.PublishedCache.HybridCache/ElementCache.cs Adds IPublishedElementCache implementation delegating to IElementCacheService.
src/Umbraco.PublishedCache.HybridCache/DependencyInjection/UmbracoBuilderExtensions.cs Registers element cache + element seed key providers + element refresh handlers.
src/Umbraco.PublishedCache.HybridCache/DatabaseCacheRebuilder.cs Updates rebuild call to include new elementTypeIds parameter.
src/Umbraco.PublishedCache.HybridCache/CacheManager.cs Adds Elements cache property and constructor parameter.
src/Umbraco.Infrastructure/Search/ContentTypeIndexingNotificationHandler.cs Updates to the new document publish-status query interface.
src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublishStatusRepository.cs Adds element publish-status query support.
src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ElementRepository.cs Emits ElementRefreshNotification on UoW refreshed entity for cache refresh flow.
src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentNavigationRepository.cs Adds multi-objectType navigation queries and supports container nodes via LEFT JOIN.
src/Umbraco.Core/Web/IUmbracoContext.cs Adds Elements to the core Umbraco context interface.
src/Umbraco.Core/Services/PublishStatus/PublishStatusService.cs Refactors into abstract base for shared publish-status cache behavior.
src/Umbraco.Core/Services/PublishStatus/PublishStatusInitializationNotificationHandler.cs Initializes both document and element publish-status services on startup.
src/Umbraco.Core/Services/PublishStatus/PublishedMediaStatusFilteringService.cs Updates comment to reference the new document publish-status interface name.
src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs Updates to consume IDocumentPublishStatusQueryService.
src/Umbraco.Core/Services/PublishStatus/IPublishStatusQueryService.cs Makes legacy interface obsolete and aliases to IDocumentPublishStatusQueryService.
src/Umbraco.Core/Services/PublishStatus/IPublishStatusManagementService.cs Makes legacy interface obsolete and aliases to IDocumentPublishStatusManagementService.
src/Umbraco.Core/Services/PublishStatus/IElementPublishStatusQueryService.cs Introduces element publish-status query interface.
src/Umbraco.Core/Services/PublishStatus/IElementPublishStatusManagementService.cs Introduces element publish-status management interface.
src/Umbraco.Core/Services/PublishStatus/IDocumentPublishStatusQueryService.cs Introduces document publish-status query interface.
src/Umbraco.Core/Services/PublishStatus/IDocumentPublishStatusManagementService.cs Introduces document publish-status management interface.
src/Umbraco.Core/Services/PublishStatus/ElementPublishStatusService.cs Implements element publish-status (init + per-key updates) on top of the base class.
src/Umbraco.Core/Services/PublishStatus/DocumentPublishStatusService.cs Implements document publish-status (incl. ancestor-path checks) and bridges obsolete interfaces.
src/Umbraco.Core/Services/Navigation/NavigationInitializationNotificationHandler.cs Rebuilds element navigation structures on startup alongside content/media.
src/Umbraco.Core/Services/Navigation/IElementNavigationQueryService.cs Adds query interface for element navigation structure.
src/Umbraco.Core/Services/Navigation/IElementNavigationManagementService.cs Adds management interface for element navigation structure.
src/Umbraco.Core/Services/Navigation/ElementNavigationService.cs Implements element navigation rebuilding for element + container object types.
src/Umbraco.Core/Services/Navigation/ContentNavigationServiceBase.cs Adds multi-objectType rebuild support for trees with mixed node types.
src/Umbraco.Core/Services/DocumentUrlService.cs Adds new constructors and migrates to IDocumentPublishStatusQueryService with obsolete shims.
src/Umbraco.Core/PublishedCache/IPublishedElementCache.cs Adds the public async-only element cache facade.
src/Umbraco.Core/PublishedCache/IMediaCacheService.cs Extracts common cache ops via IContentCacheService.
src/Umbraco.Core/PublishedCache/IElementCacheService.cs Expands element cache service contract and inherits IContentCacheService.
src/Umbraco.Core/PublishedCache/IDocumentCacheService.cs Extracts common cache ops via IContentCacheService.
src/Umbraco.Core/PublishedCache/IContentCacheService.cs Introduces shared cache-service operation interface (seed, rebuild, clear, etc.).
src/Umbraco.Core/PublishedCache/ICacheManager.cs Adds Elements property for element cache access.
src/Umbraco.Core/Persistence/Repositories/IPublishStatusRepository.cs Adds element publish-status query methods.
src/Umbraco.Core/Persistence/Repositories/INavigationRepository.cs Adds multi-objectType overloads for navigation queries.
src/Umbraco.Core/Notifications/ElementRefreshNotification.cs Introduces refresh notification used for internal cache refresh.
src/Umbraco.Core/Models/CacheSettings.cs Adds element seeding settings + element cache entry durations.
src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs Registers element navigation + document/element publish-status services (incl. obsolete aliases).
src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs Migrates to IDocumentPublishStatusQueryService with obsolete constructor shims.
src/Umbraco.Core/Constants-SqlTemplates.cs Adds SQL template constant for element source selection.
src/Umbraco.Core/Constants-Cache.cs Adds cache tag constant for elements.
src/Umbraco.Core/Cache/Refreshers/Implement/ElementCacheRefresher.cs Keeps element publish-status cache in sync during element cache refresh events.
src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs Migrates to document publish-status management service and adds new constructor shaping.

Comment thread src/Umbraco.Core/Models/CacheSettings.cs Outdated
The obsolete constructors in ApiContentRouteBuilder and DocumentUrlService
were using direct casts from IPublishStatusQueryService to
IDocumentPublishStatusQueryService, which would fail at runtime for
external consumers compiled against pre-v18 binaries. Use
StaticServiceProvider.Instance.GetRequiredService instead, consistent
with the pattern in ContentCacheRefresher.
Preserve the old constructor signatures for CacheManager,
NavigationInitializationNotificationHandler, and
PublishStatusInitializationNotificationHandler so that external
consumers compiled against pre-element-cache versions don't break.
New dependencies are resolved via StaticServiceProvider.
@lauraneto lauraneto marked this pull request as draft April 13, 2026 11:48
@lauraneto lauraneto marked this pull request as ready for review April 13, 2026 11:48
Comment thread src/Umbraco.Core/PublishedCache/ICacheManager.cs
Comment thread src/Umbraco.Core/Persistence/Repositories/INavigationRepository.cs
Comment thread src/Umbraco.Core/Persistence/Repositories/INavigationRepository.cs
@claude

claude Bot commented Apr 13, 2026

Copy link
Copy Markdown

PR Review

Target: origin/v18/dev · Based on commit: 34d1d4fd · Skipped: 0 noise files out of 73 total

Introduces an element cache for Umbraco's HybridCache layer — adds IPublishedElementCache, ElementCacheService, seeding providers, notification handlers, and the ElementPublishStatusService split from the existing PublishStatusService. Also refactors document publish status into a dedicated DocumentPublishStatusService.

  • Modified public API: ICacheManager (+Elements), IUmbracoContext (+Elements), INavigationRepository (+2 multi-key overloads), IPublishStatusRepository (+2 element methods), IPublishStatusManagementService (now [Obsolete]), IPublishStatusQueryService (now [Obsolete]), IDatabaseCacheRepository (method renames + additions)
  • Affected implementations (outside this PR): Any external package implementing ICacheManager, IUmbracoContext, INavigationRepository, or IPublishStatusRepository will fail to compile
  • Breaking changes: Four public interfaces gained new members without default implementations (see inline comments); PublishStatusService changed from public class to public abstract class
  • Other changes: New CacheSettings.ElementBreadthFirstSeedCount, ElementSeedBatchSize, CacheEntry.Element configuration; new Constants.Cache.Tags.Element tag; new IDocumentPublishStatusManagementService/IDocumentPublishStatusQueryService/IElementPublishStatusQueryService/IElementPublishStatusManagementService interfaces

Note

Complexity advisory — This PR may benefit from splitting.

  • Layer spread: Core, Infrastructure, PublishedCache.HybridCache, and Web.Common all touched across 73 files. Consider splitting the PublishStatusService refactor (document/element split + obsolete constructor chains) from the new element cache wiring — the refactor is independently reviewable and mergeable.
  • Mixed intent: The PR combines a new feature (element cache), a refactor (PublishStatus split), and new infrastructure (navigation multi-key queries). If the refactor were already merged, the element cache feature diff would be significantly smaller.

This is an observation, not a blocker. The full review follows below.


Critical

  • src/Umbraco.Core/PublishedCache/ICacheManager.cs:33: IPublishedElementCache Elements { get; } added to a public interface without a default implementation — external ICacheManager implementations won't compile. Add => throw new NotImplementedException("..."); or a StaticServiceProvider-backed default per Pattern 3 in the breaking-changes guide.
  • src/Umbraco.Core/Web/IUmbracoContext.cs:47: Same issue — Elements property on IUmbracoContext has no default.
  • src/Umbraco.Core/Persistence/Repositories/INavigationRepository.cs:22: GetContentNodesByObjectType(IEnumerable<Guid>) added without a default. Suggested: => objectTypeKeys.SelectMany(key => GetContentNodesByObjectType(key));
  • src/Umbraco.Core/Persistence/Repositories/INavigationRepository.cs:36: GetTrashedContentNodesByObjectType(IEnumerable<Guid>) — same issue.

Important

  • src/Umbraco.Core/Persistence/Repositories/IPublishStatusRepository.cs:36,44: GetAllElementPublishStatusAsync and GetElementPublishStatusAsync are new public interface members without defaults. External implementations will break. Safe defaults: return empty Dictionary/HashSet respectively.
  • src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs:83 (and ~20 other [Obsolete] attributes throughout this PR): All new [Obsolete] attributes say "Scheduled for removal in Umbraco 19" — but the current major version is 18 (per version.json). Per policy, removal must be current + 2 = v20. Affected files: ContentCacheRefresher.cs (lines 83, 121, 160), NavigationInitializationNotificationHandler.cs:49, PublishStatusInitializationNotificationHandler.cs:41,61, IPublishStatusManagementService.cs:9, IPublishStatusQueryService.cs:9, ApiContentRouteBuilder.cs:41,77, DocumentUrlService.cs:179,222, CacheManager.cs.

Request Changes

Critical and important issues must be addressed first.

@AndyButland AndyButland left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good to me @lauraneto. I spotted a few things inline for check-over.

Test coverage looks great, but I noticed for documents we have DocumentHybridCacheVariantsTests, DocumentHybridCacheScopeTests, DocumentPropertyCacheLevelTests and DocumentHybridCacheMockTests and no equivalent for elements. Maybe that's worth looking over and seeing if it makes sense to create element versions. No need to do so if you don't see any value though. The main gap looks like tests for culture variants.

I've limited myself to code review here and not done any manual testing - I think that given automated testing is solid, any further edge cases will come out in the wash as the feature as a whole is developed and tested in advance of 18 release.

There was previously a build failure but the error message referred to a file that didn't exist, so I wasn't sure what was going on there. Have pushed an empty commit to trigger the build again.

Comment thread src/Umbraco.Core/Cache/Refreshers/Implement/ElementCacheRefresher.cs Outdated
Comment thread src/Umbraco.Core/Models/CacheSettings.cs
Comment thread src/Umbraco.Core/Notifications/ElementRefreshNotification.cs
Comment thread src/Umbraco.Core/Persistence/Repositories/INavigationRepository.cs
Comment thread src/Umbraco.Core/PublishedCache/ICacheManager.cs
Comment thread src/Umbraco.Core/Web/IUmbracoContext.cs
Comment thread src/Umbraco.PublishedCache.HybridCache/Persistence/IDatabaseCacheRepository.cs Outdated
Comment thread src/Umbraco.PublishedCache.HybridCache/Services/ElementCacheService.cs Outdated
- Rename HandlePublishedAsync to HandlePublishStatusAsync in
  ContentCacheRefresher for consistency with ElementCacheRefresher
- Make ElementCacheRefresher.HandlePublishStatusAsync async to align
  with ContentCacheRefresher's pattern
- Replace inline comments with #region blocks in IDatabaseCacheRepository
- Fix double enumeration in DocumentCacheService.SeedAsync and
  ElementCacheService.SeedAsync by materializing to List before logging
Apply the same fix from #22451 (documents/media) to elements:
- ElementCacheService.RefreshElementAsync: early-return for trashed
  elements, deleting from the database cache and removing from memory.
- ElementCacheService.RefreshMemoryCacheAsync: add symmetric else
  branches so memory cache entries are removed when the database cache
  has no corresponding draft or published node (self-healing).
- Re-enable Cannot_Get_Published_Again_After_Trashing integration test.
Move Cannot_Get_Trashed_As_Published and
Cannot_Get_Published_Again_After_Trashing from
ElementPublishingServiceTests to ElementHybridCacheTests where they
belong — these test cache invalidation, not publishing behavior.

Add Cannot_Get_Published_Elements_After_Folder_Trashed to verify that
trashing an element folder clears its child elements from the published
cache.
Add ElementHybridCacheVariantsTests covering culture variant behavior
for the element cache: variant property values per culture, invariant
property consistency across cultures, single culture updates, single
culture publishing, and draft access to both cultures.

Add isElement parameter to
CreateContentTypeWithTwoPropertiesOneVariantAndOneInvariant to support
creating variant element types without a separate builder method.

@AndyButland AndyButland left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates @lauraneto - it's all looking good, though I found a few other things to consider on a second pass. Please see what you think.

Comment thread src/Umbraco.Core/Services/PublishStatus/DocumentPublishStatusService.cs Outdated
Comment thread src/Umbraco.Core/PublishedCache/IPublishedElementCache.cs
Align with the codebase convention where Id refers to integer
identifiers and Key refers to GUID identifiers.
…uivalent

Add IsPublished and IsPublishedInAnyCulture to
IDocumentPublishStatusQueryService to match
IElementPublishStatusQueryService naming.

Keep IsDocumentPublished and IsDocumentPublishedInAnyCulture as obsolete
default implementations delegating to the new methods, since
IPublishStatusQueryService (which exposes these names) ships on main.

Update all internal callers to use the new names.
Only use LEFT JOIN when the query includes container types (e.g.
element containers) which don't have umbracoContent rows. Documents
and media always have content rows, so INNER JOIN preserves query
optimizer hints for those queries.
Make GetSeedKeys virtual on BreadthFirstKeyProvider and introduce
ShouldSeed and ShouldTraverseChildren hooks so subclasses only need
to override filtering logic instead of duplicating the entire
traversal.

- Document: overrides ShouldSeed to filter unpublished nodes
- Element: overrides ShouldSeed + ShouldTraverseChildren (always
  traverse, since containers may have published children)
- Media: uses base defaults (seed and traverse everything)

Removes the 'new' hiding pattern and the V16 TODO.
@lauraneto lauraneto requested a review from AndyButland April 20, 2026 13:15

@AndyButland AndyButland left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job @lauraneto - this all looks good to me and thanks for bearing with a couple of rounds of amends. Just one conversation to resolve (which is going back on something I suggested before).

@lauraneto lauraneto enabled auto-merge (squash) April 20, 2026 14:33
@lauraneto lauraneto merged commit 498c1ce into v18/dev Apr 20, 2026
26 of 27 checks passed
@lauraneto lauraneto deleted the v18/feature/element-cache branch April 20, 2026 15:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants