Sitecore Failed to update profile pattern

My Sitecore CD servers logged the following errors 2-3 times per minute:

7760 11:10:46 ERROR Failed to update profile pattern
Exception: System.ArgumentNullException
Message: Value cannot be null.
Parameter name: item
Source: Sitecore.Kernel
at Sitecore.Diagnostics.Assert.ArgumentNotNull(Object argument, String argumentName)
at Sitecore.Analytics.Data.Items.ItemRecords`1.<GetDescendants>d__8.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at Sitecore.Analytics.Data.Items.ProfileItem.get_MappedPatterns()
at Sitecore.Analytics.Data.Items.ProfileItem.MatchPattern(IProfileData profile, IPatternDistance distance)
at Sitecore.Analytics.Tracking.Profile.UpdatePattern()

The error occurs because the editors who set up the profile and pattern cards on the solution accidentally renamed the Pattern Cards folder:

Profile and Pattern Cards

Profile and Pattern Cards

Do not rename the folder names. Sitecore uses the names to find the cards. Instead, change the Display Name if the name of the folder does not suit you.


Posted in Sitecore 8 | Tagged , , , | Leave a comment

Get Sitecore System Info

Would you like to know the System Info of your current Sitecore instance:

Sitecore System Info

Sitecore System Info

It’s fairly easy. I created an SysInfo.aspx page and dumped in into the /sitecore modules/shell folder. The page is now available on the URL <yourwebsite>/sitecore modules/shell/sysinfo.aspx.

The top info is retrieved directly from Sitecore using the Sitecore.Configuration.About.ToHtml() method. The rest is retrieved from the Sitecore Configuration:

<%@ Page Language="c#" EnableEventValidation="false" AutoEventWireup="true" EnableViewState="false" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Security.Principal" %>
<%@ Import Namespace="System.Threading" %>

<script runat="server">
  void Page_Init(object sender, System.EventArgs e)

  void Page_Load(object sender, System.EventArgs e)
    Response.Buffer = false;
    Response.BufferOutput = false;

<!DOCTYPE html>
<html xmlns="">
<head runat="server">
  <title>System Info</title>
  <form id="MainForm" runat="server"></form>
  <%# Sitecore.Configuration.About.ToHtml() %><br />
  <br />
      <td>Instance name</td>
      <td><%# Sitecore.Configuration.Settings.InstanceName %></td>
      <td>Data Folder</td>
      <td><%# Sitecore.Configuration.Settings.DataFolder %></td>
      <td>Log folder</td>
      <td><%# Sitecore.Configuration.Settings.LogFolder %></td>
      <td>Temp folder</td>
      <td><%# Sitecore.Configuration.Settings.TempFolderPath %></td>
      <td>Version file</td>
      <td><%# Sitecore.Configuration.Settings.VersionFilePath %> <%# File.Exists(Sitecore.Configuration.Settings.VersionFilePath) ? "found" : "Not found" %></td>
      <td>License file</td>
      <td><%# Sitecore.Configuration.Settings.LicenseFile %> <%# File.Exists(Sitecore.Configuration.Settings.LicenseFile) ? "found" : "Not found" %></td>
      <td>Analytics enabled</td>
      <td><%# Sitecore.Configuration.Settings.Analytics.Enabled %></td>
      <td>License ID</td>
      <td><%# Sitecore.SecurityModel.License.License.LicenseID %></td>
      <td>Sitecore.xDB.Base is present</td>
      <td><%# Sitecore.SecurityModel.License.License.HasModule("Sitecore.xDB.Base") %></td>
      <td>xDB enabled</td>
      <td><%# Sitecore.Configuration.Settings.GetBoolSetting("Xdb.Enabled", true) %></td>
  <hr />
  <%# System.Web.Helpers.ServerInfo.GetHtml() %>

To top it off I also added a call to System.Web.Helpers.ServerInfo.GetHtml() to get the .ASP Net info:

.ASP Net Server Info

.ASP Net Server Info



Please be aware that the information on a page like this is highly classified and is pure candy for any hacker. So make sure that the page is not available from outside, or at least protected by security of some kind.

I always protect pages like this using Declarative security as described in this blog post: Using Declarative Security in Sitecore. Even if the page is not available from the web.


Posted in c#, General .NET, Sitecore 5, Sitecore 6, Sitecore 7, Sitecore 8 | Tagged , | Leave a comment

Sitecore MongoDB – What do we know about a contact?

The Sitecore Analytics can be somewhat of a black box. All we know is that Sitecore stores everything about a site visitor in a Contact object and stuffs it into MongoDB when the visitor session expires.

A while ago my colleague Lasse Pedersen gave me the following scripts to be executed in RoboMongo to debug and see what Sitecore does know about my contact. The script is especially useful when working with named contacts. Replace the IDENTIFIER_NAME with the name of your contact and run it inside RoboMongo:

identifier = db.Identifiers.findOne({_id: /IDENTIFIER_NAME/i});
contact = db.Contacts.findOne({_id:});
classifications = db.ClassificationsMap.findOne({_id: contact._id });
userAgents = db.UserAgents.find({_id:{$in: classifications.Classificators.UserAgents}});
devices = db.Devices.find({LastKnownContactId: contact._id});
interactions = db.Interactions.find({ContactId: contact._id});

So what does the scripts return?


Contact Identifier

Contact Identifier

This is just the identifier information.


Contact Contact

Contact Contact

This is the complete Contact object, including all the Contact Facets that you have added yourself.


Contact Classifications

Contact Classifications

This is the data that helps Sitecore identify if the current contact is a human or a robot.


Contact Useragents

Contact Useragents

Here you will find the reference to the user agent data, and the information as to whether the contact was indeed identified as a human or a robot.


Contact Devices

Contact Devices

A device is essential the cookie that was used when the user interacted with the website. Sitecore will use these data to merge contacts as it links one Contact to every device used.


Contact Interactions

Contact Interactions

This is each interaction that the contact have had with the website.


Posted in Sitecore 8 | 1 Comment

Send email via SparkPost and C#

SparkPost is yet another email service. It is easy to work with, seems robust, and it will be fairly easy on your client’s budget, as you get 100.000 emails per month for free (as per jan 2017).

A SparkPost email is set up using a template where you specify replacement tokens using a {{tokenname}} syntax:

Sparkpost Template with token replacements

Sparkpost Template with token replacements

Sending an email is done by calling the SparkPost API endpoint. You submit a JSON document containing the recipient email address plus the replacement tokens of your choice.

So let’s send an email.

First I will create a class that can generate the JSON string containing the data to send to SparkPost. The real coder would create objects that get serialized, but for this example, a StringBuilder will do the trick. To send an email we need at least a template name (the name of the SparkPost template), a campaignID (when tracking the emails afterwards) and an email address. The rest is dynamic data (and there is much more to do than just replacing strings but let’s keep it simple):

public byte[] CreateJsonBody(string templateName, string campaignID, string email, string firstName, string lastName)
  StringBuilder sb = new StringBuilder();
  sb.AppendFormat(@"  ""campaign_id"": ""{0}"",", campaignID);
  sb.Append(@"  ""recipients"": [");
  sb.Append(@"    {");
  sb.AppendFormat(@"      ""address"": ""{0}"",", email);
  sb.Append(@"      ""substitution_data"": {");
  sb.AppendFormat(@"        ""email"": ""{0}"",", email);
  sb.AppendFormat(@"        ""firstname"": ""{0}"",", firstName);
  sb.AppendFormat(@"        ""lastname"": ""{0}""", lastName);
  sb.Append(@"      }");
  sb.Append(@"    }");
  sb.Append(@"  ],");
  sb.Append(@"  ""content"": {");
  sb.AppendFormat(@"    ""template_id"": ""{0}""", templateName);
  sb.Append(@"  }");

  byte[] body = Encoding.UTF8.GetBytes(sb.ToString());
  return body;

Next step is to create a Service that will post the data to the SparkPost service:

using System;
using System.IO;
using System.Net;
using System.Text;

namespace MyNamespace
  public class SparkPostService
    private readonly string _SPARKPOSTURL = "";
    private readonly string _sparkPostAuthorizationKey;

    public SparkPostService(string sparkPostAuthorizationKey)
      _sparkPostAuthorizationKey = sparkPostAuthorizationKey;

    public string SendEmail(byte[] jsonBody, out string contentType)
        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(_SPARKPOSTURL);

        webRequest.KeepAlive = false;
        webRequest.ServicePoint.ConnectionLimit = 24;
        webRequest.Headers.Add("UserAgent", "MyUserAgent");
        webRequest.ProtocolVersion = HttpVersion.Version10;

        ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;

        webRequest.ContentType = "application/json";
        webRequest.Accept = "application/json";
        webRequest.Headers.Add("Authorization", _sparkPostAuthorizationKey);
        webRequest.Method = WebRequestMethods.Http.Post;
        webRequest.ContentLength = jsonBody.Length;
        Stream dataStream = webRequest.GetRequestStream();
        dataStream.Write(jsonBody, 0, jsonBody.Length);
        byte[] bytes;
        using (WebResponse webResponse = webRequest.GetResponse())
          contentType = webResponse.ContentType;
          using (Stream stream = webResponse.GetResponseStream())
            using (MemoryStream memoryStream = new MemoryStream())
              byte[] buffer = new byte[0x1000];
              int bytesRead;
              while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                memoryStream.Write(buffer, 0, bytesRead);
              bytes = memoryStream.ToArray();
        return Encoding.UTF8.GetString(bytes);
      catch (Exception ex)
        throw new Exception(GetType() + "Failed to retrieve data from '" + _SPARKPOSTURL + "': " + ex.Message, ex);


To send an email I can now do the following:

string sparkPostKey = (found in SparkPost);
string contentType;

SparkPostService service = new SparkPostService(sparkPostKey);
string result = service.SendEmail(CreateJsonBody("my-first-email", "myCampaign", "", "Brian", "Pedersen"), out contentType);

The SparkPost authorization key is created inside SparkPost.

SparkPost will now send an email where my name and email is substituted:

Sparkpost Email

Sparkpost Email


Posted in c#, General .NET | Tagged , | Leave a comment

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:

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

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");
      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)
        Log.Info(this + ". Clearing " + cache.Count + " items from " + cacheName, this);

    private readonly ArrayList _caches = new ArrayList();
    public ArrayList Caches
        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.


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.



Posted in c#, General .NET, Sitecore 6, Sitecore 7, Sitecore 8 | Tagged , , , , , , , | 5 Comments

Sitecore General error when submitting contact – Another contact with the same identifier already exists

In Sitecore when creating your own contacts you can get the following exception:

10604 10:16:27 ERROR General error when submitting contact.
Exception: System.InvalidOperationException
Message: Another contact with the same identifier already exists.
Source: Sitecore.Analytics.MongoDB
   at Sitecore.Analytics.Data.DataAccess.MongoDb.MongoDbDataAdapterProvider.SaveContactWithIdentifier(IContact contact, ContactSaveOptions saveOptions)
   at Sitecore.Analytics.Data.DataAccess.MongoDb.MongoDbDataAdapterProvider.<>c__DisplayClass9.<SaveContact>b__7()
   at Sitecore.Analytics.Data.DataAccess.MongoDb.MongoDbDataAdapterProvider.Try(Action action)
   at Sitecore.Analytics.Data.DataAccess.MongoDb.MongoDbDataAdapterProvider.SaveContact(IContact contact, ContactSaveOptions saveOptions)
   at Sitecore.Analytics.Data.ContactRepository.SaveContact(Contact contact, ContactSaveOptions options)
   at Sitecore.Analytics.Tracking.ContactManager.SubmitContact(Contact contact, ContactSaveOptions options, Contact& realContact)

The error can occur if you try to flush a contact with a new contact ID but with an existing identifier. It usually is a result of Sitecore having a mismatch between what the ContactRepository and the MongoDB contains.

This pseudocode explains the situation:

ContactRepository contactRepository = Factory.CreateObject("tracking/contactRepository", true) as ContactRepository;
ContactManager contactManager = Factory.CreateObject("tracking/contactManager", true) as ContactManager;
// Contact is loaded from repository but repository cannot find it
Contact contact = contactRepository.LoadContactReadOnly(username);
// ... so we try to create it
if (contact == null)
  // Contact is created in memory
  contact = contactRepository.CreateContact(ID.NewID);
 contact.Identifiers.Identifier = username;
  // And we try to write it to xDB (MongoDB)
  // but the contact is already there. 

The problem arises as the FlushContactToXdb does not throw an exception, so you are left with the impression that the Contact is created and everything is fine.

If you are using the ExtendedContactRepository as I described in the blog post Sitecore Contacts – Create and save contacts to and from xDB (MongoDB), you risk that the GetOrCreateContact method goes in an infinite loop because:

  1. We try to load the contact, but the contact is not found
  2. We Create a new contact
  3. We flush the contact, but the flush method fails silently
  4. We call step 1

To avoid the infinite loop, please see Dan’s suggestion to how to solve it:

Or you can add a retry counter in the class.

Add the following 2 lines to the class:

private const int _MAX_RETRIES = 10;
private int _retries;

Then modify the private method GetOrCreateContact:

private Contact GetOrCreateContact(string username, LockAttemptResult<Contact> lockAttempt, ContactRepository contactRepository, ContactManager contactManager)
  switch (lockAttempt.Status)
	case LockAttemptStatus.Success:
	  Contact lockedContact = lockAttempt.Object;
	  lockedContact.ContactSaveMode = ContactSaveMode.AlwaysSave;
	  return lockedContact;

	case LockAttemptStatus.NotFound:
	  Contact createdContact = CreateContact(username, contactRepository);
	  Log.Info(string.Format("{0}: Flushing contact '{1}' (contact ID '{2}') to xDB.", GetType().Name, createdContact.Identifiers.Identifier, createdContact.ContactId), this);

	  // NEW CODE: Check for retries, and throw an exception if retry count is reached
	  if (_retries >= _MAX_RETRIES)
		throw new Exception(string.Format("ExtendedContactRepository: Contact {0} could not be created. ", username));

	  return GetOrCreateContact(username);

	  throw new Exception("ExtendedContactRepository: Contact could not be locked - " + username);

This is still experimental code, but as described before, so is the ExtendedContactRepository.



Posted in .net, c#, General .NET, Sitecore 8 | Tagged , , , , , | 2 Comments

Sitecore Scheduled Tasks – Run on certain server instance

If you would like certain Sitecore Scheduled Tasks run on a certain server instance (your CM server for example) the approach is pretty simple.

First, create a new folder under /sitecore/system/Tasks. I have a new folder called “CMSchedules“. Place your Scheduled Tasks in this folder.

Scheduled Tasks in separate folder

Scheduled Tasks in separate folder

Then tell Sitecore to execute these tasks. Place the following configuration on the instance you wish should run these tasks. Do not place the configuration on servers that should not run the tasks.

You need to patch the Sitecore.config (for Sitecore 8.1 and up) or web.config (for versions prior to Sitecore 8.1). Add the following to the /sitecore/scheduling section:

<agent type="Sitecore.Tasks.DatabaseAgent" method="Run" interval="00:05:00">
  <param desc="database">master</param>
  <param desc="schedule root">/sitecore/system/Tasks/CMSchedules</param>

The configuration tells Sitecore to execute all tasks in the /sitecore/system/Tasks/CMSchedules every 5 minutes.



Posted in Sitecore 5, Sitecore 6, Sitecore 7, Sitecore 8 | Tagged , | 3 Comments

Sitecore Publishing – Programmatically determine if item should be published

Sitecore uses it’s publish settings to determine if an item should be published. But you can only control a publish from versions and date times.

Sitecore Publishing Settings

Sitecore Publishing Settings

So what if you have other values that determine if an item must be published or not? Say, a status field or a checkbox, or a combination of both? And what do you do if these fields are updated from external systems?

One way is to extend the publishItem pipeline. Every item that must be published goes through this pipeline one by one and you can therefore add a new processor that determines if an item is published or not.


These are our publish options: A status field and a checkbox is updated by users and external systems to determine if this item is eligible for publishing.

Publish Options

Publish Options

The publishItem pipeline works “the other way round”, meaning that if the item can be published we leave it alone, and if not we change the publish status to “DeleteTargetItem“.

Here is my new processor (some pseudocode, apply your own checks):

using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Publishing;
using Sitecore.Publishing.Pipelines.PublishItem;

namespace MyCode.Infrastructure.Pipelines.PublishItem
  public class DetermineActionForItem : PublishItemProcessor
    public override void Process(PublishItemContext context)
      // First a list of checks to ensure that
      // it is our item and we can make the decision
      // about it's publish status
      if (context == null)
      if (context.Aborted)
      Item sourceItem = context.PublishHelper.GetSourceItem(context.ItemId);
      if (sourceItem == null)
      if (!sourceItem.Paths.IsContentItem)
      // I will only check our specific item
      if (!sourceItem.TemplateName == "mytemplate")

      // OK, now we know that this is our item and
      // we can determine it's faith
      // Check every language to see if it is eligible for publishing
      foreach (Language language in sourceItem.Languages)
        Item languageVersion = sourceItem.Versions.GetLatestVersion(language);
        // A little pseudocode, here is the check to see if the item can be published
        if (StatusIsOK(languageVersion["StatusField"]) && CheckBoxIsChecked(languageVersion["CheckBoxField"])
          // Yes, the item can be published

      // No, the item cannot be published, set the publishaction == DeleteTargetItem
      Log.Info(string.Format("{0}: Unpublishing Item '{1}' from database '{2}' because it is not in the correct state.",
      context.Action = PublishAction.DeleteTargetItem;

As you can see, the function returns if the item being processed is not our item, or if the item can be published. If the item is not eligible for publishing, we change the Action to DeleteTargetItem.

The processor is added to the publish pipeline just before the Sitecore “DetermineAction” processor:

<publishItem help="Processors should derive from Sitecore.Publishing.Pipelines.PublishItem.PublishItemProcessor">
  <processor type="Sitecore.Publishing.Pipelines.PublishItem.RaiseProcessingEvent, Sitecore.Kernel"/>
  <processor type="Sitecore.Publishing.Pipelines.PublishItem.CheckVirtualItem, Sitecore.Kernel"/>
  <processor type="Sitecore.Publishing.Pipelines.PublishItem.CheckSecurity, Sitecore.Kernel"/>
  <!-- Our Processor inserted before Sitecore's "DetermineAction" proecssor -->
  <processor type="MyCode.Infrastructure.Pipelines.PublishItem.DetermineActionForItem, MyDll" />
  <!-- ... -->
  <processor type="Sitecore.Publishing.Pipelines.PublishItem.DetermineAction, Sitecore.Kernel"/>



Posted in General .NET, Sitecore 6, Sitecore 7, Sitecore 8 | Tagged , , , | Leave a comment

Sitecore Event Queue – How to clean it – and why

Yesterday the dreaded Sitecore Event Queue almost killed me again – well it certainly almost killed my CM server. The server went from being busy but stable to being unresponsive. CPU and memory load skyrocketed:

A fully loaded CM server

A fully loaded CM server

Obviously it happened after a system update, so after a panic debug and rollback the system owner pointed out: “Hey, the event queue table is quite big?“.
Of course, the system updated flooded the event with 1.5 million events, and the problem did not go away because I keep 1 day of events in the queue.


First we need to stabilize the system, then we need to update the configuration.


The following SQL statement will clean out the history table, publish queue and event queue, leaving only 12 hours of history and publish data and 4 hours of events. Replace YOURDATABASE with the name of your database:

/****** History ******/
delete FROM [YOURDATABASE_Core].[dbo].[History] where Created < DATEADD(HOUR, -12, GETDATE())
delete FROM [YOURDATABASE_Master].[dbo].[History] where Created < DATEADD(HOUR, -12, GETDATE())
delete FROM [YOURDATABASE_Web].[dbo].[History] where Created < DATEADD(HOUR, -12, GETDATE())
/****** Publishqueue ******/
delete FROM [YOURDATABASE_Core].[dbo].[PublishQueue] where Date < DATEADD(HOUR, -12, GETDATE());    
delete FROM [YOURDATABASE_Master].[dbo].[PublishQueue] where Date < DATEADD(HOUR, -12, GETDATE());
delete FROM [YOURDATABASE_Web].[dbo].[PublishQueue] where Date < DATEADD(HOUR, -12, GETDATE());
/****** EventQueue ******/
delete FROM [YOURDATABASE_Master].[dbo].[EventQueue] where [Created] < DATEADD(HOUR, -4, GETDATE())
delete FROM [YOURDATABASE_Core].[dbo].[EventQueue] where [Created] < DATEADD(HOUR, -4, GETDATE())
delete FROM [YOURDATABASE_Web].[dbo].[EventQueue] where [Created] < DATEADD(HOUR, -4, GETDATE())


With the system stabilized, we need to take more care of the table sizes.


Sitecore is already configured to clean the tables so they only contain 12 hours of data. 12 hours of data is usually what any SQL server will handle, and you will have up to 10.000 rows in the table.

  <obj type="Sitecore.Data.$(database).$(database)HistoryStorage, Sitecore.Kernel">
    <param connectionStringName="$(id)" />


Sitecore keeps 30 days of publish queue. If you insert and update items often, you should lower this number. For each item change (including any change that the system does) is stored here.

<agent type="Sitecore.Tasks.CleanupPublishQueue, Sitecore.Kernel" method="Run" interval="04:00:00">


The event queue is the most important table to keep small. In a distributed environment, each server will read the contents of the table every 5 seconds, using a time stamp stored in the Properties table as key. Any row before the time stamp will not be read.

You therefore need enough history to cater that a server will be offline for a while, but at the same time so little contents that any read and write will be amazingly fast.

If you can keep the number of rows below 7.000, most SQL server should be able to handle that amount of data. Even smaller numbers are preferred as well.

Before Sitecore 8.1, Sitecore would only allow you to clean events older that 1 day. This is way too much, especially if you publish often. The new IntervalToKeep will allow you to determine the hours to keep as well:

<agent type="Sitecore.Tasks.CleanupEventQueue, Sitecore.Kernel" method="Run" interval="04:00:00">



After these changes, my server is back to normal, busy but responsive:

Normal Load

Normal Load



Posted in Sitecore 8 | Tagged , , , , | 2 Comments

Sitecore Measure Pipeline performance and profile processors

In Sitecore, Pipelines are by far my favorite dependency injection pattern, and I have used them since Sitecore 5. One of the secret features of pipelines are the built in profiling. Oh, yes, by flipping 2 switches you can measure the performance of your pipeline processors.


Enable the \App_Config\Include\Sitecore.PipelineProfiling.config.disabled by removing the .disabled extension.

Or add the following 2 settings to your /App_Config/sitecore.config:

<setting name="Pipelines.Profiling.Enabled" value="true" />
<setting name="Pipelines.Profiling.MeasureCpuTime" value="true" />

The Pipelines.Profiling.MeasureCpuTime is not enabled by default in the Sitecore.PipelineProfiling.config file as it introduces a performance overhead, so only use this setting on your test environment.


Sitecore comes with a profiling page (it has been there since Sitecore 7 i guess):


Pipeline Profiler

Pipeline Profiler

The page displays all pipelines executed since you pressed the refresh button, how many times it has been executed, and the average wall time (wall time = real-world time from processor started to it finished, as opposed to CPU time which is the time spend by the CPU executing the processor) per execution.



Posted in Sitecore 7, Sitecore 8 | Tagged , , , | Leave a comment