One area of Sitecore that doesn’t seem to get much attention is the Sitecore prefetch cache. Recently we’ve been investigating how to get the content editor working faster, especially with large content dbs. Under the hood Sitecore has prefetch caches for each database it uses. If you are tuning your site be sure to pay them some attention. The config files it uses live in /app_config/prefetch.
A word of caution before you begin, updating using the following approach can increase the app startup time dramatically. I’d recommend only applying within live environments as you don’t want to add minutes to app startup during dev. Another thing to be wary of is the cache sizes, during testing it wasn’t uncommon to reach 2000MB of data in the pre-fetch cache. If you tune up the capacities make sure you pay attention to other caches eg master[items] to ensure balance throughout the caching layer.
What does the prefetch cache give you out the box?
The ability to tune:
- the size of the prefetch cache per database
- the number of child items per children call
- which items to cache
- which items to load the children of
- which items of template type to load
Depending on the setup of your Sitecore tree a combination of the above will need tuning.
Some tips
-
Speed up the CMS (master.config) media library by caching all the media folders:
1<template desc="media folder">{FE5DD826-48C6-436D-B87A-7C4210C7413B}</template> -
Get a clear view of what is in all your caches with the more detailed admin page:
https://marketplace.sitecore.net/en/Modules/Sitecore_Cache_Admin.aspx
What else can you do?
In most of our builds we setup a file which contains all the template ids we use in the app. An example would be:
1 2 3 4 5 6 7 8 9 10 11 |
using System; namespace ###.Cms { public static class TemplateKeys { public static readonly Guid Homepage = new Guid("{4AC5A50D-FC94-4421-909F-A1F7567CB4B8}"); public static class Settings { public static readonly Guid BreadcrumbNav = new Guid("{20022ACD-1D2F-4C19-A8E8-322713BB734D}"); |
This allows quick access to all the templates when asserting on specific content types. The following code maps this kind of file to the notation required by the prefetch cache configuration. When you run the page it will generate all the <template name=’… entries for the master.config.
Front end:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="PrefetchCacheGenerator.aspx.cs" Inherits="###.Presentation.Website.Testing.Prefetching.PrefetchCacheGenerator" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:TextBox runat="server" ID="DebugTextBox" TextMode="MultiLine" Width="1000" Height="1000"></asp:TextBox> </div> </form> </body> </html> |
Back end:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using ###.Cms; namespace ###.Presentation.Website.Testing.Prefetching { public partial class PrefetchCacheGenerator : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { DebugTextBox.Text = GenerateXml(); } private static string GenerateXml() { //TemplateKeys is our file containing all the templateId's Type templateKeysType = typeof (TemplateKeys); IEnumerable<Type> templateTypes = GetSubclasses(templateKeysType); StringBuilder builder = new StringBuilder(); foreach (Type templateType in templateTypes) { foreach (PrefetchCacheEntry cacheEntry in LoadEntries(templateType, templateType.FullName + "_")) { builder.AppendFormat("<template name=\"{0}\">{1:B}</template>", cacheEntry.Key, cacheEntry.Value); builder.AppendLine(); } } return builder.ToString(); } private static IEnumerable<Type> GetSubclasses(Type templateKeysType) { foreach (Type type in templateKeysType.Assembly.GetTypes()) { if (type.FullName.Contains(templateKeysType.Name)) { yield return type; } } } private static IEnumerable<PrefetchCacheEntry> LoadEntries(Type type, string prefix) { foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static)) { if (fieldInfo.FieldType == typeof (Guid)) { yield return new PrefetchCacheEntry {Key = prefix + fieldInfo.Name, Value = Guid.Parse(fieldInfo.GetValue(null).ToString())}; } } } } public class PrefetchCacheEntry { public string Key { get; set; } public Guid Value { get; set; } } } |
How to build on this?
How about generating the config file during app startup?
Other info.
There is a detailed video from Sitecore at http://mediacontent.sitecore.net/wmv/CacheRecordingWebinar.mp4 about the caches. Alternatively read the Sitecore docs
Have you never been interested in why sitecore is issuing checks like “… [ItemId] IN (SELECT [ID] …” which are performing extremely bad? Such kind of SQL statements make the database to work well up to hundreds of records but can show noticeable performance drops when tables reac even thousands of rows! Not to speak when they accumulate hundreds thusands of rows or even more!
These sort of things are probably worth bringing up with support. In the past we’ve run diagnostics onto the db’s and found in the most part that the indexes we’d expect are in place.
One set of queries which we’ve seen to cause lots of rows being returned relate to when you have lots of languages and lots of versions as you get back fields for every permutation.