11

I'm just mapping out some caching for a module which loads data from an external SQL database.

I want to cache the results of methods for a configured amount of time but if the data is reimported on the CM I want to clear the cache on the CDs (as well as the CM).

I'd planned on using a named Sitecore cache (I have some code I've used before which will do nicely for this).

So my question is: How after the data import routine do I tell the CD servers that the named cache is invalid?

2 Answers 2

17

I would use a remote event that can be raised on the CM server and subscribed to by the CD servers. When the event is raised, an event handler can execute on the CD servers clearing the cache.

This is the mechanism used by the HtmlCacheClearer handler that comes with Sitecore, you can see how it is setup in the default Sitecore.config:

<event name="publish:end">
  <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
    <sites hint="list">
      <site>website</site>
    </sites>
  </handler>
</event>
<event name="publish:end:remote">
  <handler type="Sitecore.Publishing.HtmlCacheClearer, Sitecore.Kernel" method="ClearCache">
    <sites hint="list">
      <site>website</site>
    </sites>
  </handler>
</event>

The publish:end event is raised on the local environment when publishing ends (i.e. the CM server), and the publish:end:remote event is raised on remote environments (i.e. the CD servers).

It is fairly straight-forward to setup but requires a bit of boilerplate code. It makes use of two mechanisms in Sitecore:

  • Event Raising / Handling, which allows you to configure code that subscribes to an event and executes when that event is raised.
  • The Event Queue, which allows for events to be raised on one machine and handled by another.

You are able to create your own custom events in Sitecore just by adding them to configuration. First of all, you can define your new event and your cache clearing class in configuration like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <event name="dataimport:complete">
      <handler type="MyProject.DataImporter.CacheClearer, MyProject.DataImporter" method="ClearCache" />
    </event>
    <event name="dataimport:complete:remote">
      <handler type="MyProject.DataImporter.CacheClearer, MyProject.DataImporter" method="ClearCache" />
    </event>
  </sitecore>
</configuration>        

In this example, I have left out the implementation of MyProject.DataImporter.CacheClearer, but it is here that you would perform the functionality for clearing your cache.

Once you have this, raising this event can be done through code, which you can do once your import is complete. The following code will raise both the local event as well as the remote one:

// Raise the local event
Event.RaiseEvent("dataimporter:complete");

// Add some data to the Event Queue, which will be consumed by other instances and then raised as events on those instances.
Sitecore.Eventing.EventManager.QueueEvent<DataImportCompleteEvent>(new DataImportCompleteEvent());

In this case, DataImportCompleteEvent is simply an empty class and used as a means of a class for the remove instance to listen to. You can also use this class and add parameters to it if you want data to be passed to the remote instances as part of the event. I will not add that to this example to keep it simple.

The remote event doesn't fire automatically without further setup. You will also need to write some code that tells each instance to listen out for events of the DataImportCompleteEvent class and raise the dataimporter:complete:remote event locally on that machine.

public class DataImportEventHandler
{
  public virtual void Initialize(PipelineArgs args)
  {
    var action = new Action<DataImportCompleteEvent>(RaiseRemoteEvent);
    Sitecore.Eventing.EventManager.Subscribe<DataImportCompleteEvent>(action);
  }
  private void RaiseRemoteEvent(DataImportCompleteEvent myEvent)
  {
    Sitecore.Events.Event.RaiseEvent("dataimporter:complete:remote");
  }
}

To make sure this code is executed at startup, you can add a processor to the initialize pipeline:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor type="MyProject.DataImporter.DataImportEventHandler, MyProject" method="Initialize" />
      </initialize>
    </pipelines>
  </sitecore>
</configuration>

The Sitecore community documentation also contains an example that is similar to this, and may provide assistance if you want to add parameters to your event - http://sitecore-community.github.io/docs/pipelines-and-events/events/

3

As you are mentioning data in an external database, have you considered SqlCacheDependency? I've used this in a few occasions when caching data related to a (custom) SQL database was involved. You can find some info on msdn.

Using SQL cache dependency, you could cache your information and create a dependency on a database table or row change. When the data changes—and only then—the cache items based on that data are invalidated and removed from the cache. The next time you request that item from the cache, if it is not in the cache, you can re-add the updated version to the cache and be assured that you have the latest data.

I have used this mostly in a scenario where I keep a separate table that has timestamps indicating the latest data change. Whenever I want to clear caches, I update the timestamp. All servers (that registered themselves) get notified and will clear their cache.

This solution is outside Sitecore, but available in asp.net and sql. Worked perfectly for me a few times already.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.