Caching Manager in Sitecore 8

March 30, 2015

It's been a while since I've updated the Caching Manager, which is a module on the Sitecore Marketplace, so during a recent upgrade it was about that time. I'm using the design from Sitecore 8. It's backwards compatible through Sitecore 6 (SIM makes this possible) so there's no excuse for not getting it. Unless, you're on 5? Was caching on 5? I honestly don't remember or probably never knew.

The what now?

If you've never seen the Caching Manager, it's a single .aspx file that can be used to view your cache settings from a running Sitecore instance. Sitecore provides a default at: /sitecore/admin/cache.aspx... buuut it's kinda meh. Well, really meh. When I started using caching, I couldn't clear small chunks out when I needed to, so I built what I needed. Just for comparison, here's the default:

original cache manager

The Caching Manager is a bit different. It gives a few general sizes but also a search form and some clear options:

  • Search All Cache
  • Clear Search Results (whaaaaaaa)
  • Clear All Cache

Home

Are there more Features?

Well, right, yes,

  • Sitecore 8 design aesthetic (ooh doesn't it feel new)
  • Sitecore protected page (requires login so it's good on prod)
  • Separate tabs for different types of cache (more focused search)
  • Search for either summaries of a cache or it's individual entries
  • Select all buttons! (it's such a help)

My favorite tab "Caches By Site" lists the site caches by type: filtered items, html, viewstate, xsl. System sites are separated from custom sites so you can select those quickly too (kinda grainy, sorry).

Sites

"Caches by Database" similarly allows you to search by type: blogIDs, data, items, itempaths, paths, standardValues. This image shows individual entries.

Database

I believe "Access Result Cache" stores obfuscated user data so you won't get much from it. I added it to be consistent and for what limited control you can have.

Access Result

I separated Data Provider Caches just because there were so many. I never use it, but you might!

Data Provider

Miscellaneous is just that. 

Miscellaneous

 

Have you? It's okay if you haven't.

If you haven't used HTML caching (I have no clue how many do, but I'm curious to know) you should at least know how to start using it (aside from using the Caching Manager, which I highly recommend).

Select a standard values item with presentation details applied and select the top nav "Presentation Tab". Then click on "Details" (closer to the left).

Presentation Details Button

In the list of the sublayout controls, click on one. ex: BasicPageContent

Presentation Details

You'll see the properties for that control. If you collapse the "General" tab or scroll down, you'll see the "Caching" section. Clicking "Cacheable" makes it cacheable (of course you'll have to publish these settings for web). There's also a sub-list of "Vary by" settings and one event trigger "Clear on Index Update" which seems pretty self explanatory. 

Sublayout Caching Properties

The "Vary by" settings controls how many variations of the page are stored in cache. This page type, for example, will render HTML differently depending on the instance item. In this case it makes sense to "Vary by Data" so that all pages don't display the first page of this template type entered into cache. You can optionally store versions specific to the device, user, etc. 

HTML Cache

While we're on the subject of caching, let's talk about HTML cache, and clearing said cache (invalidating for pedants). In my day-to-day I'm mostly concerned with how the HTML cache performs. I prefer to have as little "thrashing" in the cache as possible; meaning if I publish one item, then I won't need to clear the entire HTML cache. This is how the default behavior works. In fact, the default behavior expects you to add entries to the config for each site you want cleared on publish events. I don't see the need in all that duplication. The sites already exist elsewhere and I want to cache them all. 

To rectify my needs with the will of Sitecore, I patch over a few cache clearing events to wire up my own class. The class will attempt to find the site that contains the root item being published by comparing content start paths with the item path. I'd considered trying to line up media library items to the site too but each system could be different in that respect. Consider it for yourself though. 

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
	<sitecore>
		<events timingLevel="custom">
			<event name="publish:end">
				<handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
					<patch:attribute name="type">Sample.Publishing.HtmlCacheClearer, Sample</patch:attribute>
				</handler>
			</event>
			<event name="publish:end:remote">
				<handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
					<patch:attribute name="type">Sample.Publishing.HtmlCacheClearer, Sample</patch:attribute>
				</handler>
			</event>
		</events>
	</sitecore>
</configuration>

Here's the class they reference:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Sitecore.Diagnostics;
using Sitecore.Sites;
using Sitecore.Configuration;
using Sitecore.Caching;
using Sitecore.Events;
using Sitecore.Publishing;
using Sitecore.Data.Events;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Web;

namespace Sample.Publishing {
    public class HtmlCacheClearer {
        // Properties
        private readonly ArrayList _sites = new ArrayList();
        public ArrayList Sites {
            get {
                return this._sites;
            }
        }

        public HtmlCacheClearer()
            : base() {
            _sites = new ArrayList();
        }

        // Methods
        public void ClearCache(object sender, EventArgs args) {
            Assert.ArgumentNotNull(sender, "sender");
            Assert.ArgumentNotNull((object)args, "args");

            Item rootItem = null;
            string argsType = args.GetType().ToString();

            if (argsType.Equals("Sitecore.Data.Events.PublishEndRemoteEventArgs")) { //for remote endpoints
                PublishEndRemoteEventArgs pargs = (PublishEndRemoteEventArgs)args;
                ID did = new ID(pargs.RootItemId);
                Assert.IsNotNull(did, "publish root item id");
                Database db = Sitecore.Configuration.Factory.GetDatabase(pargs.TargetDatabaseName);
                if (db == null) {
                    Log.Info("HtmlCacheClearer: " + pargs.TargetDatabaseName + " db is null", this);
                    return;
                }

                rootItem = db.GetItem(did);
            } else if (argsType.Equals("Sitecore.Events.SitecoreEventArgs")) { //for local endpoints
                SitecoreEventArgs sargs = (SitecoreEventArgs)args;
                object[] p = sargs.Parameters;
                if (p.Length > 0 && p[0] != null) {
                    Publisher per = (Publisher)p[0];
                    rootItem = per.Options.RootItem;
                }
            }

            if (rootItem == null) {
                Log.Info("HtmlCacheClearer: root item is null", this);
                return;
            }

            SiteContext sc = GetSiteContext(rootItem);
            if (sc == null) {
                Log.Info("HtmlCacheClearer: site context was null", this);
                return;
            }

            HtmlCache htmlCache = CacheManager.GetHtmlCache(sc);
            if (htmlCache != null) {
                ((CustomCache)htmlCache).Clear();
                Log.Info("HtmlCacheClearer: cleared " + sc.Name, this);
            } else {
                Log.Info("HtmlCacheClearer: html cache for " + sc.Name + " was null", this);
            }
        }

        public SiteContext GetSiteContext(Item i) {
            IEnumerable<SiteContext> list = Factory.GetSiteInfoList()
                .Select(a => Factory.GetSite(a.Name))
                .Where(a => !a.StartPath.Equals(string.Empty) && i.Paths.FullPath.Contains(a.StartPath));
            return (list.Any()) ? list.First() : null;
        }
    }
}

??????!!!!!!!!??????!!?..........

What are you looking at me for? Go get that ish