question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Cache level of property value converters seems to be ignored

See original GitHub issue

I’ve tested this in 7.15.0, 8.1.0, 8.4.0 and 8.6.0. It appears that this has never really worked - or at least since 7.15.0, but Umbraco 8 still works differently than Umbraco 7.

The problem appears to be that the value returned by the GetPropertyCacheLevel method in a property value converter (PVC) is largely ignored.

Umbraco 7

In Umbraco 7, the PropertyCacheLevel enum is defined with the following values:

  • Content
    Indicates that the property value can be cached at the content level, ie it can be cached until the content itself is modified.

  • ContentCache
    Indicates that the property value can be cached at the content cache level, ie it can be cached until any content in the cache is modified.

  • Request
    Indicates that the property value can be cached at the request level, ie it can be cached for the duration of the current request.

  • None
    Indicates that the property value cannot be cached and has to be converted any time it is requested.

But regardless of what value my dummy PVC returns, it appears to always behave as Request. My PVC is implemented as following:

using System;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;

namespace CacheLevelTests {
    
    public class CacheLevelTestsValueConverter : IPropertyValueConverterMeta  {

        public bool IsConverter(PublishedPropertyType propertyType) {
            return propertyType.PropertyEditorAlias == "CacheLevelTests";
        }

        public object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) {
            return Guid.NewGuid();
        }

        public object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) {
            return source;
        }

        public object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) {
            return null;
        }

        public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue) {
            return PropertyCacheLevel.None;
        }

        public virtual Type GetPropertyValueType(PublishedPropertyType propertyType) {
            return typeof(Guid);
        }

    }

}

For testing, I’ve installed Umbraco with the default starter kit, added a property to the Home content type. I’ve changed the Home.cshtml view to contain the following:

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage<ContentModels.Home>
@using ContentModels = Umbraco.Web.PublishedContentModels;
@{

    string value1 = Model.Content.GetPropertyValue<string>("tests");

    string value2 = Umbraco.TypedContent(Model.Content.Id).GetPropertyValue<string>("tests");

    <pre>@value1</pre>
    <pre>@value2</pre>

}

When the cache level is set to None, I would assume value1 and value2 would differ (as the PVC returns a random GUID each time caclled), but they’re always the same. Both variables are updated with the same new random GUID once I reload the page.

But more importantly, regardless of the cache level returned my PVC, the example always shows a new GUID when I reload the page. For both Content and ContentCache, it out to require on editor saving something in the backoffice.

Umbraco 8

In Umbraco 8, the PropertyCacheLevel enum is instead defined with the following values:

  • Unknown
    Not really a lot of description about this one in the documentation 🤷

  • Element
    Indicates that the property value can be cached at the element level, i.e. it can be cached until the element itself is modified.

  • Elements
    Indicates that the property value can be cached at the elements level, i.e. it can be cached until any element is modified.

  • Snapshot
    Indicates that the property value can be cached at the snapshot level, i.e. it can be cached for the duration of the current snapshot.

    In most cases, a snapshot is created per request, and therefore this is equivalent to cache the value for the duration of the request.</remarks>

  • None
    Indicates that the property value cannot be cached and has to be converted each time it is requested.

So besides the addition of Unknown and that the names have changed a bit for Umbraco 8, the concept is similar to how it worked in Umbraco 7.

But while Umbraco 7 always appears to use Request regardless of the cache level returned by the PVC, Umbraco 8 seems to use Element instead.

This means that regardless of the cache level, it requires the exact element (here the home node) to be updated for the cache to be cleared. Simply reloading the page or updating another page in Umbraco doesn’t change anything.

I tested this in a similar way to Umbraco 7, so here is my dummy PVC:

using System;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;

namespace CacheLevelTests {
    
    public class CacheLevelTestsValueConverter : PropertyValueConverterBase {

        public override bool IsConverter(IPublishedPropertyType propertyType) {
            return propertyType.EditorAlias == "CacheLevelTests";
        }
        
        public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) {
            return Guid.NewGuid();
        }
        
        public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) {
            return inter;
        }

        public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) {
            return null;
        }

        public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) {
            return PropertyCacheLevel.None;
        }

        public override Type GetPropertyValueType(IPublishedPropertyType propertyType) {
            return typeof(Guid);
        }

    }

}

And my home.cshtml views looks as following:

@inherits Umbraco.Web.Mvc.UmbracoViewPage<ContentModels.Home>
@using ContentModels = Umbraco.Web.PublishedModels;
@{

    string value1 = Model.Value<string>("tests");

    string value2 = Umbraco.Content(Model.Id).Value<string>("tests");

    <pre>@value1</pre>
    <pre>@value2</pre>

}

Why does this matter?

In Umbraco 7, it appears that the values returned by PVCs are only cached for the scope of a request, which may explain why it hasn’t been discovered before (at least I haven’t found an issue about it).

But for PVCs doing performance heavy lookups, it’s crucial that these values can be cached. So the end result is that Umbraco 7 uses too little caching, which then may affect performance.

On the other hand, Umbraco 8 caches the values too hard. For instance if we have a content picker, it’s PVC specifies the cache level as Elements. This should mean that updating any page in Umbraco should clear the cache.

But as the cache level seems to be treated as Element instead, it requires the specific page with the content picker to be updated.

This then means, that if we make changes to any of the pages selected in the content picker, the changes aren’t reflected on the page with the content picker.

So for a site we’re currently working on, we’re running this code on each publish:

private void ContentService_Published(IContentService sender, PublishEventArgs<IContent> e)
{
    var pages = e.PublishedEntities;
    foreach (IContent page in pages)
    {
	try
	{
	    // Use code from internal RefreshAllContentCache https://github.com/umbraco/Umbraco-CMS/blob/13488be0a3280c966ca5115c1cf75cc1979e5819/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs#L107
	    var payloads = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
	    Umbraco.Web.Composing.Current.DistributedCache.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
	}
	catch
	{
	}
    }
}

It feels very dirty having to do this, but it’s done out of necessity.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:3
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
nul800sebastiaancommented, Dec 18, 2020

I don’t know either, maybe the unit tests for it are a good start? https://github.com/umbraco/Umbraco-CMS/blob/v8/contrib/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs

I can see we moved from IPublishedCache to IPublishedCache2 for 8.7 (https://github.com/umbraco/Umbraco-CMS/pull/8273/), so maybe it’s been improved there?

0reactions
MahmoudAbdelsallamcommented, Dec 28, 2022

I added this code to refresh cache. After some investigation it appeared that cache is refreshed when we update clientDependency.config file. So I added custom componet for update this file. ` public class ClearCacheOnPublishComponent : IComponent { private readonly ILogger _logger;

public ClearCacheOnPublishComponent(ILogger logger)
{
  _logger = logger;
}

public void Initialize()
{
  ContentService.Published += ContentService_Published;
}

private void ContentService_Published(IContentService sender,
    ContentPublishedEventArgs e)
{
  var clientDependencyConfig = new ClientDependencyConfiguration(_logger);
  var clientDependencyUpdated = clientDependencyConfig.UpdateVersionNumber(
      UmbracoVersion.SemanticVersion, DateTime.UtcNow, "yyyyMMdd");
}

public void Terminate()
{
  //unsubscribe during shutdown
  ContentService.Published -= ContentService_Published;
}

}`

Read more comments on GitHub >

github_iconTop Results From Across the Web

content cache not always updated on azure app service
After much investigation, I found out about an issue with the cache level in property value converters being ignored.
Read more >
Application Data Caching
The @CacheKey annotation will be ignored when the foo cache data is invalidated, but param1 will be the cache key when the bar...
Read more >
Learn to avoid an identity jump issue (IDENTITY_CACHE) ...
In this article, we will examine the Identity Cache feature and the identity jump issue. Sometimes we see that identity jumps by some ......
Read more >
Spring Boot converter ignored for nested property
If the nested property is a string for exemple, no problem. But here spring boots seems to completely ignore my converter. If the...
Read more >
Enabling API caching to enhance responsiveness
You can enable API caching in Amazon API Gateway to cache your endpoint's responses. With caching, you can reduce the number of calls...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found