Sitecore custom cache that is cleared on publish

In this article I will demonstrate how to create a custom Sitecore cache and how to ensure that it is cleared when you publish.

First I will create the simplest custom cache available:

namespace MyNamespace
{
  public class MyCustomCache : CustomCache
  {
    public MyCustomCache(string name, long maxSize) : base("MyCustomCache." + name, maxSize)
    {
    }

    public string Get(string cacheKey)
    {
      return GetString(cacheKey);
    }

    public void Set(string cacheKey, string value)
    {
      SetString(cacheKey, value);
    }
  }
}

The cache is instantiated with a name, meaning that you can use the class to create more than one cache, as long as you remember to give each instance its own name.

The cache contains a string as key and returns a string as value. It is used like this (pseudocode):

// Instantiation, set up an 1MB cache
private MyCustomCache myCustomCache = new MyCustomCache("CacheName", StringUtil.ParseSizeString("1MB"));

// Get value from cache
string myValue = myCustomCache.Get(myKey);

// Set value in cache
myCustomCache.Set(myKey, myValue);
  

To clear the cache on publish, we need to set up an event handler on either publish:end and publish:end:remote or publish:complete and publish:complete:remote.

There is a little confusion as to when in the publish pipeline these events are fired. In previous versions (prior to Sitecore 7.2), publish:end and publish:end:remote was fired for each language and each target, and publish:complete and publish:complete:remote was fired when the publish job was done. But in later versions, publish:end and publish:end:remote is also only fired once.

The :remote events (publish:end:remote and publish:complete:remote) is fired on your remote (i.e. CD servers) by the way.

Enough talk, lets code the cache clearer. First I will set up the 2 new events on publish:end and publish:end:remote:

<events>
  <event name="publish:end">
    <handler type="MyNamespace.MyCacheClearer, Mydll" method="ClearCaches">
      <caches hint="list">
        <cache>MyCustomCache.CacheName</cache>
      </caches>
    </handler>
  </event>
  <event name="publish:end:remote">
    <handler type="MyNamespace.MyCacheClearer, Mydll" method="ClearCaches">
      <caches hint="list">
        <cache>MyCustomCache.CacheName</cache>
      </caches>
    </handler>
  </event>
</events>

The events will now call the method ClearCaches on the Mynamespace.MyCacheClearer class:

namespace MyNamespace
{
  public class MyCacheClearer 
  {
    public void ClearCaches(object sender, EventArgs args)
    {
      Assert.ArgumentNotNull(sender, "sender");
      Assert.ArgumentNotNull(args, "args");
      try
      {
        DoClear();
      }
      catch (Exception ex)
      {
        Log.Error(this + ": " + ex, ex, this);
      }
    }

    private void DoClear()
    {
      foreach (string cacheName in Caches)
      {
        Cache cache = CacheManager.FindCacheByName(cacheName);
        if (cache == null)
          continue;
        Log.Info(this + ". Clearing " + cache.Count + " items from " + cacheName, this);
        cache.Clear();
      }
    }

    private readonly ArrayList _caches = new ArrayList();
    public ArrayList Caches
    {
      get
      {
        return this._caches;
      }
    }
  }
}

The DoClear() method uses the <caches> list from the configuration to find which caches to clear. You should write the names of each of the caches to be cleared in the configuration.

FINAL NOTES:

The caches are first created when instantiated and the first element is added. That is why it is not available in the /sitecore/admin/cache.aspx administration tool on Sitecore startup.

MORE TO READ:

 

About briancaos

Developer at Pentia A/S since 2003. Have developed Web Applications using Sitecore Since Sitecore 4.1.
This entry was posted in c#, General .NET, Sitecore 6, Sitecore 7, Sitecore 8 and tagged , , , , , , , . Bookmark the permalink.

13 Responses to Sitecore custom cache that is cleared on publish

  1. Hey Brian, interestingly in 8.2 I _never_ see the cache in the `cache.aspx` page using this method, even after things are added. The admin page asks the `CacheManager` to display all its configured caches via the method `GetAllCaches()`. When I execute that in a watch window, there’s 122 (including mine) but the admin page only reports 117 – 5 are going missing somewhere and I’ve no idea why… yet

    Like

  2. OK Brian, it turns out that the CacheAdmin page goes looking for an ICacheInfo interface and the CustomCache base class does not actually implement this. The whole thing is a facade anyway so you can easily implement the ICacheInfo by forwarding all the members to `this.InnerCache`.

    Like

  3. UPDATE :: Brian if you’re reading this you can delete the above comments. Anyone else, I was looking in the wrong spot (I had two app domains – best not to ask why). Interesting side-notes: 1) If you override `Clear()` on your CustomCache it is *not* called by the Admin page; 2) There doesn’t appear to be any hook to respond to a `CacheClear` event anywhere; 3) ICacheInfo is implemented in an internal `Cache` object that is connected to your named cache by the `CacheManger`; 4) If you want Sitecore to use your cache class instead of the internal one you need to implement the ICache interface and add a child element to the config node with the same name as your cache and normal type specifier conventions (it uses the std Sitecore factory)… phew!

    Like

  4. That last comment should have a reference to an xml element called “cacheContainerConfiguration” but it was filtered out. In 8.2 (only) find it in the “CacheContainers.config.example” file

    Like

  5. Pingback: Sitecore Caching – Clear caches individually | Brian Pedersen's Sitecore and .NET Blog

  6. Pingback: Sitecore publish:end and publish:end:remote | Brian Pedersen's Sitecore and .NET Blog

  7. Pingback: Which of my old Sitecore posts are still valid in Sitecore 9? | Brian Pedersen's Sitecore and .NET Blog

  8. owenwattley says:

    I know this is an old post, but I’m having some issues finding out what decides which database’s EventQueue table the “publish:complete:remote” event goes into.

    I’ve got a CM and 2 CDs, each with their own web database (web1 and web2). After I publish to both, the “publish:complete:remote” seems to go into the core database, which only the CM and 1 of the CDs has access to (the other CD is in a different region so cannot share the core db – it has its own core db).

    Is there some config or class I can override to specify that I want that event to be delivered to the relevant web database’s EventQeue instead?

    Like

  9. briancaos says:

    You are definitely on to something regarding the publish:complete:remote event. Although it has nothing to do with any custom cache settings.

    The queue mechanisms in Sitecore are purely database based. An event is written to a database (core,master,web, depending on the event type), and a listener service on each server uses a timestamp from the Properties table (also located in core,master,web) to determine which events are new and should be handled.

    The information on whether the CD servers need access to the CORE database is ambiguous. According to Sitecore, you should be able to disable the CORE database from a CD instance, as long as you move the users away from the core database:

    https://doc.sitecore.com/developers/92/platform-administration-and-architecture/en/disable-the-core-database-on-a-content-delivery-instance.html

    But other articles state that a CD server needs access to the CORE and WEB databases, because the CORE database contains vital event information (like the publish/remote events).

    To be honest, I don’t know what is true, and I believe that Sitecore might have changed the dependency on the CORE database between versions, so I think you should consult Sitecore Support and hear if your version supports a CORE-less CD environment.
    I for one would be very interested in hearing what they have to say.

    Like

  10. owenwattley says:

    Thanks for the fast reply @briancaos!

    I understand and agree with all the points you made (and, yes, apologies that my comment is nothing to do with caching directly – but it is related as I’m trying to ensure a custom cache is cleared on my CD2 when “publish:complete:remote” fires).

    What is most interesting to me is this bit:

    “An event is written to a database (core,master,web, depending on the event type)”

    Do you know, specifically, where the logic that decides what this eventtypedatabase mapping is? Is it hard coded in each event or something like that? I’m having a really hard time trying to understand how it works – it seems like a total mystery.

    Thanks again for your insight.

    I might just resort to using “publishing:end:remote” as that seems to go into the correct web databases for each CD.

    Like

  11. briancaos says:

    I believe that the database in which the events will end up in, are somewhat hard coded. I haven’t seen any configuration where this can be changed. A classic SItecore-way-of-thought would be that the event somehow follows the data, i.e. system events goes into CORE, item create/update/delete events would end up in the MASTER database, and item publish events would end up in the WEB database. However most Sitecore rules are like the pirates code – more like guidelines. Welcome aboard!

    Like

  12. owenwattley says:

    😂 Classic Sitecore. Many thanks.

    Like

  13. Pingback: Fix Custom Cache after Sitecore upgrade – Jeroen's Sitecore Blog

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.