Performance: Skip the content cache rebuild when a document type property is removed - #23330
Conversation
|
Claude finished @AndyButland's task in 8m 59s —— View job PR ReviewTarget: Adds
Important
Suggestions
Approved with Suggestions for improvementGood to go, but please carefully consider the importance of the suggestions. The correctness argument is solid: published content is always resolved against the current content type definition, so an orphaned blob value for a removed property is simply never mapped. The Id-based guard in |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new “raw data unaffected” content-type change flag so that document type property removals no longer trigger expensive cmsContentNu rebuilds, while still performing the other RefreshMain side-effects (e.g., published content type cache refresh / indexing triggers).
Changes:
- Added
ContentTypeChangeTypes.RawDataUnaffectedplus helper extensions (RequiresRawDataRebuild,RequiresConvertedCacheClearOnly) to route rebuild vs. converted-cache clearing. - Updated the three cache-handling paths (in-scope, deferred, and distributed) to skip raw-data rebuild when the change is a pure property removal.
- Added unit + integration coverage ensuring property removal hides published values without rebuilding the stored blob, while alias/composition changes still rebuild.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.HybridCache/DeferredCacheRebuildNotificationHandlerTests.cs | Adds coverage that RawDataUnaffected changes do not queue deferred rebuilds. |
| tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.HybridCache/CacheRefreshingNotificationHandlerTests.cs | Verifies rebuild is skipped and converted cache is cleared for RawDataUnaffected changes. |
| tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ContentTypeChangeExtensionsTests.cs | Tests the new change-type helper methods for rebuild vs. clear-only behavior. |
| tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/ContentTypeCacheRefresherTests.cs | Ensures distributed refresher clears converted cache without rebuilding memory cache for RawDataUnaffected. |
| tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheDocumentTypeTests.cs | Integration proof: property removal hides published values without cmsContentNu rebuild; guards for alias/composition/batch cases. |
| src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/DeferredCacheRebuildNotificationHandler.cs | Queues deferred rebuilds only when raw-data rebuild is actually required. |
| src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/CacheRefreshingNotificationHandler.cs | Routes between rebuild vs. clear-converted-only and adds debug logging for rebuild/deferral decisions. |
| src/Umbraco.Core/Services/ContentTypeServiceBase{TRepository,TItem}.cs | Classifies property-removal-only structural changes as RawDataUnaffected with an Id-based guard against false positives in batch/composition scenarios. |
| src/Umbraco.Core/Services/Changes/ContentTypeChangeTypes.cs | Introduces the new RawDataUnaffected flag on the public enum. |
| src/Umbraco.Core/Services/Changes/ContentTypeChangeExtensions.cs | Adds the new change-type helper methods used by handlers/refreshers. |
| src/Umbraco.Core/Cache/Refreshers/Implement/ContentTypeCacheRefresher.cs | Distributed routing: rebuild memory cache only when raw-data rebuild is needed; otherwise clear converted cache selectively. |
…introduces a property with the same alias.
|
Zeegaan
left a comment
There was a problem hiding this comment.
I think this makes good sense, great too see all the tests too 🙌



Description
Follow-up to the content-cache-rebuild investigation. Removing a property from a document type currently triggers a full rebuild of the published database cache (
cmsContentNu) for every document of that type — expensive on large sites — even though the removal does not actually change what published content exposes.A property removal does not require a
cmsContentNurebuild: published content is always resolved against the current content type, so a removed alias simply stops mapping to a property and its (now orphaned) value in the stored blob is never read. This PR skips the rebuild for that case and only clears the converted in-memory cache instead.How it works
ContentTypeChangeTypes.RawDataUnaffected— an additive flag that supplementsRefreshMain. It is set (inComposeContentTypeChanges) only when a property removal is the sole structural cause of a change (no alias change, variation change, composition removal, etc.).RefreshMainis retained, so everything else keyed off it is unchanged — search re-indexing, published-content-type cache clearing and model-factory reset all still run. Only the rawcmsContentNurebuild is skipped.CacheRefreshingNotificationHandler(in-scope),DeferredCacheRebuildNotificationHandler(deferred), andContentTypeCacheRefresher(distributed / other servers) rebuild only whenRequiresRawDataRebuild(), otherwise just clear the converted cache.ComposeContentTypeChanges(keyed by content type Id) ensures a type that also independently needs a rebuild — e.g. a batch save where it inherits a renamed property via a composition — is never flagged.Why the flag rather than reclassifying to
RefreshOtherRefreshMaindrives more than thecmsContentNurebuild (notably Examine re-indexing). Reclassifying a removal asRefreshOtherwould silently turn those off, and would misrepresent a destructive change. The additive flag keeps allRefreshMainhandling and lets only the rebuild step opt out. It is also backwards- and rolling-upgrade-tolerant (a not-yet-upgraded node treats the change as a normalRefreshMain).Notes
Testing
Automated
Solution builds and CI checks should pass. Adds unit tests for the new change-type helpers and the three handlers' routing, and integration tests proving a removed property is no longer exposed by any published read path while the stored blob is provably not rebuilt, that alias/composition changes still rebuild, and that the batch multi-type guard holds.
Manual
These steps verify from the log that removing a property skips the
cmsContentNurebuild, while another structural change (e.g. a variation change) still triggers it.1. Enable debug logging
Add a Serilog level override for the cache-refreshing handler in
appsettings.json(orappsettings.Development.json) and restart the site:{ "Serilog": { "MinimumLevel": { "Override": { "Umbraco.Cms.Infrastructure.HybridCache.NotificationHandlers.CacheRefreshingNotificationHandler": "Debug" } } } }Log output goes to the console and to
umbraco/Logs.2. Prepare
Have (or create) a document type with at least two properties and at least one document of that type; optionally publish it.
3. Remove a property → no rebuild
Edit the document type, delete a property, and save. Expect a converted-cache clear and no rebuild line:
You should not see any
Content type change: rebuilding the document database cache ...line for this save.Functional check: the removed property is no longer exposed by the published content — e.g.
@Model.Value("<alias>")returns nothing, and the Delivery API response omits it — while the remaining properties are unaffected.4. Structural change (variation) → rebuild still happens
Edit the same document type, change its variation setting (toggle Allow varying by culture), and save. Expect the rebuild line (Immediate mode, the default):
(Renaming a property's alias is a simpler alternative trigger if you'd rather not change variance — it produces the same
rebuilding ...line.)5. (Optional) Deferred mode
If the site runs
Umbraco:CMS:Cache:ContentTypeRebuildMode = Deferred, the structural change from step 4 instead logs that the rebuild is handed off, followed by the background rebuild's own Information-level lines:A property removal (step 3) in deferred mode still logs only the converted-cache clear — it is never queued for a deferred rebuild.
Media types: the same behaviour applies to media types; the log lines read
Media type change: ....