Tuesday, January 10, 2012

Expiring Content from the v8.5 Cache Framework


We in Ektron Engineering have been spending the past several years making our APIs and developer framework more user friendly and powerful with every release (see a webinar Framework API:Consistency, Discoverability, and Simplicity). We are extremely excited about the release of 8.5, and are working on anew era of development tools to complement our existing product set.
In v8.5, a transparent caching layer has been implemented. Thisnew caching layer sits below the Framework API and caches frequently accessed objects (see related webinar: v8.5 &Caching Best Practices). One common question is: "How do Invalidate the cache of a content object upon publish or update?"
Using Ektron CMS Extensions (plug-ins) you can modify the publish process to invalidate the framework cache. For those of you not familiar with either concept, allow me to provide a brief rundown on what this functionality can do.

CMS Extensions and the Strategy Pattern

Strategy Pattern is an design pattern that lets you select an implementation at runtime. For Ektron, CMS Extensions are a piece of code that is executed when a certain event fires, for example if you want to append a date to the title of your content, you could tie code to do this on the OnBeforePublish event. Now every time content is published it is executed through the strategy and a date will be appended.

The Framework API

Ektron's newest API that exposes the majority of feature sets in a straight forward way with the ability to use criteria to pull data, perform impersonation, integrate with WCF, and more.
With the most recent improvements to the Framework API, we have implemented an entire caching strategy to specify which objects we would like to automatically be stored in cache, and for how long.By doing this we greatly increase the framework performance by avoiding database hits until the cache is expired. Now this is all well and good, until we run into an instance where we need something to display immediately. Currently there are limited options to expire the cache in this scenario, you would either need a script that expires the cache entirely or by key (which would be difficult to discover), or (YIKES!) have to reset the IISapplication pool to see your content change immediately.
With the introduction of Unity into our Framework, we expose some extremely advanced caching options that solve this. The following is a step by step tutorial on creating a strategy that expires an individual cache based on content changes making cache keys dependent on changes, rather than time elapsed.
To get started let's make sure we have the necessary requirements setup within our CMS400 instance:
  • CMS400 version 8.5
  • Folder in workarea with more than one piece of content
  • Framework Caching turned on
While the first two steps are straight forward, the third step implies a little additional help. To turn on framework caching within your 8.5 I'll introduce you to a new config file in the root of your site, "ektron.cms.framework.unity.config". Upon opening this file you'll notice a few things, a list of framework class definitions, and 3 sections that define WCF, Cache, andDefault. Right now we're focused on using the Cache definition, since we're looking to cache our Framework functions. To turn on the framework caching, there should be a section in your web.config that looks like:
  <ektron.framework.services>
    <unity configSource="ektron.cms.framework.unity.config" />
    <framework defaultContainer="Default" childContainer="BusinessObjects" />
  </ektron.framework.services>
By simply changing the defaultContainer to "Cache" we have enabled it. You're framework api calls should now all be caching!You can set the timeouts individually on items by looking back inektron.cms.framework.unity.config, but the default should be okay for now.
So we have our framework caching now which is all well and good, but if we go back to our earlier scenario, we don't want to publish an item and wait around for the cache to expire, or have to expire the cache ourselves which could drastically hurt your site performance if the cache is emptied at once. With 8.5 fear no more, we can now expire the cache of items pulled through the framework api individually! We accomplish this by setting up a strategy, and for those of you that have done this before it's easier than ever in 8.5.
You can get an Ektron strategy up and running faster than ever ,you no longer need to compile the code, or use a utility to add it to your site. In a few painless steps you'll have as many hooks into your CMS system as you need. We'll start by creating anew Class file under your App_Code\CSCode, copy the code below replacing all text in the newly created CS file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Ektron.Cms;
using Ektron.Cms.Common;
using Ektron.Cms.Instrumentation;
using Ektron.Cms.Extensibility;
using Ektron.Cms.Extensibility.Content;

namespace Cms.Extensions.Custom
{
  public class InvalidateFrameworkCache : ContentStrategy
  {
    public override void OnAfterPublishContent(ContentData contentData, CmsEventArgs eventArgs)
    {
      //Cache manager class to access Framework Cache
      Ektron.Cms.BusinessObjects.Caching.CacheManager cmanager = 
          new Ektron.Cms.BusinessObjects.Caching.CacheManager();

      //Fill cachekey in
      string CackeKey = string.Format("Ektron:content:{0}", contentData.Id);

      //Remove key from cache
      cmanager.Remove(CackeKey);

      //Write results to log
      Log.WriteInfo(string.Format("InvalidateFrameworkCache.OnAfterAddContent: {0}::{0}", 
         contentData.Id, contentData.Title));
    }
  }
}
Looking at the above code we immediately can see how simple this task is using our API's. The other thing you'll probably notice is how the cache key is formatted, we follow a very simple naming convention: "Ektron:object:id". We follow this convention and use our API's to make this "smart" functionality.What I mean by that is that all the places in the cache where this object shows, are now removed. So in three lines of code, based on the piece of content we just published, it has been removed from all the caches it resided in, this includes any list caches the content was in as well. The final step is to register this extension in the object factory so that it knows to execute. To register the extension you simply need to openObjectFactory.config (also in the root of your website) and change the content section to look like the following assuming this is anew install:
<add name="Content">
  <strategies>
    <add name="GoogleGeoCoder"       
      type="Cms.Extensions.GoogleGeoCoder.ContentStrategy, Cms.Extensions.GoogleGeoCoder"/>
    <add name="InvalidateFrameworkCache" type="Cms.Extensions.Custom.InvalidateFrameworkCache" />
   </strategies>
</add>
And there you have it! With a few very simple steps we have created a strategy to invalidate the cache of a content object upon publish. Hopefully by now your imagination is cooking up more ideas on how to improve your site performance and content delivery at the same time, please leave those ideas, or other questions in the comments below!

2 comments:

  1. hey Andrew,
    great post. I've been trying to implement this on 8.6.1 but the "cmanager.Remove(CackeKey);" call really doesn't seem to do anything. is this still the same in 8.6.1?

    Thanks!

    ReplyDelete
  2. Hi Andrew,

    I know this is similar to what has already been asked, but I am also having trouble getting the code to work in 8.61. Do you know if the naming convention has changed for the Cache Key?

    Cheers

    ReplyDelete