Custom Cache

Created: 10 Mar 2016, last update: 24 Jun 2017

Sitecore Caching and Sitecore Custom Cache

This article gives an overview about Sitecore Caching and how to create your own caching in Sitecore. Instead of your own caching Framework or the .NET MemoryCache or System.Runtime.Caching, HttpRuntime.Cache you can use Sitecore Caching.
Sitecore has several caches. See How Sitecore caching work

You can see the Sitecore cache on /sitecore/admin/Cache.aspx.  It does the job but there are more beautiful tools like Caching manager in sitecore 8/ Or for development use Sitecore Rocks a Visual Studio plugin.

Fine tune your cache optimally and improve performance of your application caching. See Sitecore Caching guide http://sdn.sitecore.net/upload/sitecore6/sc62keywords/cache_configuration_reference_a4.pdf
See also #Tip 26  No Cache Limiet

<!--  CACHING - DISABLE CACHE SIZE LIMITS
            If true, Sitecore does not limit cache size growth and ignores any maximum cache sizes 
            specified in the web.config file.            
            Enabling this setting can improve the application's performance in 64-bit environments 
            by allowing Sitecore to take full advantage of the available memory.
            After setting this value to true, monitor the system at regular intervals, as this 
            configuration can cause Sitecore to consume too much memory and cause Out Of Memory errors.
            It is only recommended to set the setting to true in 64-bit environments.
            Default value: false 
-->
    <setting name="Caching.DisableCacheSizeLimits" value="false"/>

The Html cache is one of the caches you need to know as developer. Because you can configure the Sublayouts and Renderings to use the HTML Cache and improve the performance. But be careful if you use placeholders inside placeholder, nested placeholders. If you want to personalize or test a Component don’t cache a control with a placeholder because then the compleet component with the nested controls is cached. Each website has its own html cache. See Basics of html caching/

You can create your own custom “var by” for Example:
Var By IP Custom cache criteria with mvc
Var By Cookie Sitecore sublayout caching vary by cookie/
Var By Personalized Data Caching rendering html when conditional
Var By Custom Type OptimizedQuery/Sitecore.HtmlCache
Or add a TimeSpan Add sliding expiration on sitecore rendering cache/
Customizing Html Caching with Sitecore with Mvc


Default the HTML cache is cleared after a publish. Depend on your config, see the publish:end and publish:end:remote event. And you can set the “Clear on Index Update”.

When HTML Cache does not fit
For example, If you need to cache a backend call with other flush cache moment, or you need a have query on many places.

An Easy way is to abuse the HTML Cache

    var site = Context.Site;
    var cacheKey = "MyHtmlCacheKeyOnSite-" + site.Name;
    var cache = CacheManager.GetHtmlCache(site).GetHtml(cacheKey);
    if (string.IsNullOrEmpty(cache))
    {
    cache = "cache this string"
    var time = new TimeSpan(0,1,0,0);
    //cache for max 1 hour after the last cache hit, the Time span is optional
    CacheManager.GetHtmlCache(site).SetHtml(cacheKey, cache,time);
    }
    Return cache;

Or create your own Sitecore Cache, with own name, size and flush moments. You can use Sitecore.Caching.Cache.GetNamedInstance("The-Cache-Name", StringUtil.ParseSizeString("2MB")); it gives you a new instance or if exist the existing one with the supplied name. 

// Decompiled with JetBrains decompiler
// Type: Sitecore.Caching.Cache
public static Cache GetNamedInstance(string name, long defaultMaxSize)
 {
     Assert.ArgumentNotNullOrEmpty(name, "name");
     return CacheManager.FindCacheByName(name) ?? new Cache(name, defaultMaxSize);
   }

See Two Quick Tips on Sitecore Caching or use the Cache constructor

Example with string caching and subscribe on publish:end event in code for flushing the cache. Using the Sitecore.Caching.Cache Constructor.

using Sitecore;
using Sitecore.Caching;
using Sitecore.Reflection;

namespace CacheDemo
{
    public static class CacheDemo
    {
        static readonly Cache miraCache = new Sitecore.Caching.Cache("mirabeau-custom-cache", StringUtil.ParseSizeString("2MB"));

        static CacheDemo()
        {
            //flush the cache after a publish.
            Sitecore.Events.Event.Subscribe("publish:end", ClearCache_OnPublishEnd);
            Sitecore.Events.Event.Subscribe("publish:end:remote", ClearCache_OnPublishEnd);
        }

        static void ClearCache_OnPublishEnd(object sender, System.EventArgs eventArgs)
        {
            miraCache.Clear();
        }

        public static string GetMyValue(int key)
        {

            if (miraCache != null)
            {
                var cachekey = Context.Site.Name + Sitecore.Context.Database.Name + key;

                string returnString = (string)miraCache.GetValue(cachekey);
                if (string.IsNullOrEmpty(returnString))
                {
                   returnString = "cache this string key=" + key;
                    miraCache.Add(cachekey, returnString);
                    // use this for caching max 5 minutes  miraCache.Add(cachekey, returnString, TypeUtil.SizeOfString(returnString), DateTime.UtcNow.AddMinutes(5));
                    // for expire after 1 hour no cache hit use this miraCache.Add(cachekey, returnString, TypeUtil.SizeOfString(returnString), new TimeSpan(0,0,1,0));
                }
                return returnString;
            }
            return string.Empty;
        }

    }
}

Your own cache in the admin tool

See also Sitecore community github docs Fundamentals/Caching/

Or inherit from Sitecore.Caching.CustomCache
Example with expire caching an ICacheable object and inherit from CustomCache

using System;
using Sitecore;
using Sitecore.Caching;

namespace CacheDemo
{
    //the object model for the cache
    public class CacheValue : ICacheable
    {
        public CacheValue(string value)
        {
            Value = value;
        }

        public string Value { get; set; }

        public long GetDataLength()
        {
            if (Value == null)
            {
                return Sitecore.Reflection.TypeUtil.SizeOfObject();
            }
            return Sitecore.Reflection.TypeUtil.SizeOfString(Value);
        }

        public bool Cacheable { get; set; }
        public bool Immutable
        {
            get { return true; }
        }
        public event DataLengthChangedDelegate DataLengthChanged;
    }


    // Sitecore.Caching.CustomCache cannot be directly instantiated
    public class MiraCache : CustomCache
    {
        public MiraCache(string name, long maxSize)
            : base(name, maxSize)
        {
        }

        public new CacheValue GetValue(string key)
        {
            return (CacheValue)base.GetObject(key);
        }
    }

    // static else the .net garbage collector will throw it away.
    public static class MiraCacheManager
    {
        private static readonly MiraCache Cache;

        static MiraCacheManager()
        {
            Cache = new MiraCache("mirabeau-customcache-cache", StringUtil.ParseSizeString("50MB"));
            //Scavengable the scavengable caches task can clear the expirated caches. and give the memory back.
            Cache.InnerCache.Scavengable = true;
        }

        public static CacheValue GetCache(string key)
        {
            return Cache.GetValue(key);
        }

        public static void SetCache(string key, CacheValue value, DateTime absExpirationDateTime)
        {
            Cache.InnerCache.Add(key, value, value.GetDataLength(), absExpirationDateTime);
        }

        public static void SetCache(string key, CacheValue value, TimeSpan slidingExpiration)
        {
            Cache.InnerCache.Add(key, value, value.GetDataLength(), slidingExpiration);
        }

        public static void SetCache(string key, CacheValue value)
        {
            Cache.InnerCache.Add(key, value);
        }

    }
}

//use the cache this way:
MiraCacheManager.SetCache("thekey",new CacheValue("helo"),DateTime.UtcNow.AddSeconds(30));

The Cachekey
Consider what is the right key for you, typically it is somethings like “name-id-” + Sitecore.Context.Database.Name + Context.Site.Name;

Object Size
For some methods you need to know the size of the string/object. Sitecore has some helpers for that:
Sitecore.Reflection.TypeUtil.SizeOfString(mystring) and Sitecore.Reflection.TypeUtil.SizeOfObject()
Calculating the size of an object is not easy see of-memory-and-strings but for the Sitecore caching it is not breaking or overflow when the size is incorrect. But for managing and for limiting it is nice to have a good indication of the real size.

Cache an Object or String
There are different methods for adding a string or object to the cache. For caching an object your model can inherit from Sitecore.Caching.ICacheable then you can make the GetDataLength method. Without the iCacheable it is also possible to cache the object

Cache expiration
When using a TimeSpan or an expiration DateTime  you can give the memory free by setting the InnerCache.Scavengable = true; default every 3 minutes there is a scavenged job running.

<!--  SCAVENGE INTERVAL
Determines how often scavengable caches are scavenged.
Default value: 3 minutes.
-->
<setting name="Caching.ScavengeInterval" value="00:03:00" />

When using an expiration DateTime use a UTC dateTime, for Example DateTime.UtcNow.AddMinutes(3).
The CacheManager use WeakReference to avoid cleanup by the .NET garbage collector, keep the Cache object alive, make it static.

Flush Cache
Flushing the cache can be triggered by and event See Sitecore-community.github events subscript to an event by code or in the config. And call the flush method of your cache.

Distributed cache
The current Sitecore caching works fast it is in-process memory caching. Fast for single server solutions. A distributed cache on the other hand is a scalable solution multiple content servers can share the same cache and you can always add more servers to increase cache space and throughput. For example Azure Redis Cache. Today the Sitecore cache is not provider based and it is not possible to replace.

Related Blogs and links

Sitecore-caching-utility

Cache Configuration  Reference

Improving performance caching (not the Sitecore cache example)