Sitecore 6.3 Performance
One of the things I remember being touted about Sitecore “Twin Peaks” which became Sitecore 6.3 was support for massive amounts of content. We’re talking thousands of child items in a single location. I’ve always worked to the rule of trying to keep the number of child items in any location to well under 100 items, and I don’t normally see more than about 30. John West recently also wrote some guidance about the number of child items one should aim for in the Information Architecture part of his Sitecore Best Practises series. This normally just means creating more folders to segment the content further. For example, if the site has a news section I wouldn’t just have the news items created under the news root item, but instead have an “item saving” event handler which moves the news item into an “archive” folder structure based on the year, month and day the news item is for. We wrote our own event handler here at Next Digital to handle this but John West has created the “News Mover” shared source module which does the same. Now aside from the performance implications of having a huge number of items in a single location there is also a usability issue. Requiring your authors to scroll through hundreds of items in the content tree is not good usability, even with different icons on the items (and in the case of news items all icons would be the same…). So regardless of whether Sitecore performs well or not with 1000 items, you should probably still try to limit the number of items anyway. But I was curious to see if Sitecore 6.3 really had delivered on this and so I did some performance testing between Sitecore 6.2 rev 100104 and Sitecore 6.3 rev 100716. For this performance test I used my good old trusty Revolver command prompt module which quickly lets me script out repetitive tasks. Unfortunately the current version of Revolver doesn’t contain a repetition command (well, it does against existing content through the find
command); that’s flagged for Revolver 2. So to do what I wanted I just had to create 2 custom commands. The first of the commands I wanted just had to execute another command a specified number of times. Here’s the code for the Repeat
command I created.
using System;
using Revolver.Core.Commands;
using Revolver.Core;
using System.Collections.Specialized;
using System.Text;
namespace sc63Perf
{
public class Repeat : BaseCommand
{
public override string Description()
{
return "Repeat a command a number of times";
}
public override HelpDetails Help()
{
var details = new HelpDetails();
details.Usage = "<cmd> count command";
details.Description = Description();
details.AddParameter("count", "The number of times to invoke the command");
details.AddParameter("command", "The command to execute");
details.AddExample("<cmd> 5 pwd");
return details;
}
public override CommandResult Run(string[] args)
{
StringDictionary namedParameters = null;
string[] numberedParameters = null;
Util.ExtractParameters(out namedParameters, out numberedParameters, args);
if (numberedParameters.Length < 1)
return new CommandResult(CommandStatus.Failure, "Missing required parameter 'count'");
if(numberedParameters.Length < 2)
return new CommandResult(CommandStatus.Failure, "Missing required parameter 'command'");
int count = 0;
var parsed = int.TryParse(numberedParameters[0], out count);
if(!parsed)
return new CommandResult(CommandStatus.Failure, "Parameter 'count' is not a valid integer");
string command = numberedParameters[1];
var output = new StringBuilder();
for (var i = 0; i < count; i++)
{
output.Append(Context.ExecuteCommand(command, FormatContext));
}
return new CommandResult(CommandStatus.Success, output.ToString());
}
}
}
I also need a way to create a unique name for each item I’m creating. Although I could try using the existing string manipulation commands I felt it would make more sense to simply create a command to increment an integer.
using System;
using Revolver.Core;
using Revolver.Core.Commands;
namespace sc63Perf
{
public class Increment : BaseCommand
{
public override string Description()
{
return "Increment an integer";
}
public override HelpDetails Help()
{
var details = new HelpDetails();
details.Description = Description();
details.Usage = "<cmd> input";
details.AddParameter("input", "An integer to process");
details.AddExample("<cmd> 4");
return details;
}
public override CommandResult Run(string[] args)
{
if (args.Length < 1)
return new CommandResult(CommandStatus.Failure, "Missing required parameter 'input'");
int num = -1;
var parsed = int.TryParse(args[0], out num);
if (parsed)
return new CommandResult(CommandStatus.Success, (++num).ToString());
else
return new CommandResult(CommandStatus.Failure, "Parameter 'input' is not a valid integer");
}
}
}
I can use the touch
command to create an item. I’ll base the name on an integer which I’ll increment for the next run. The repeat
command above only accepts a single command to repeat, so I’ll have the create a Revolver script which will create the item and increment the name. Revolver scripts are stored in the core DB under the /sitecore/system/modules/revolver/scripts
item. I’ll create a script called create
with the following commands:
touch -t (sample/sample item) $a$
set a < (inc $a$)
The first line creates the item and uses the data in the environment variable a
as the name. The next line increments the value in the environment variable a
and stores it back. To get started I’ll need to bind my custom commands into my Revolver window.
bind sc63Perf.Repeat,sc63Perf rep
bind sc63Perf.Increment,sc63Perf inc
And before I start I’ll have to set the a
environment variable to 1 so my first item will be named 1.
set a 1
The first test was for creating the items which I could run the other tests against. To create the items I can use the repeat
command I bound to the moniker rep
above.
timer (rep 50 create)
For each Sitecore version I created 50, 100, 200, 500, 1000 and 2000 items. Here’s the results. As you can see Sitecore 6.3 shows good performance improvements on 6.2 at just over two thirds the time up the higher end. The next test was to query for a single item in those just created by ID.
timer (query *[@@id='{2729EE96-3F93-42C7-9AB2-81DB5FAC9A02}'] pwd)
Sitecore 6.3 shows huge improvements on 6.2 at less than half the time for this query. The next test was to query for the same item by name
timer (query 500 pwd)
Some quite odd artefacts at the lower end, but Sitecore 6.3 consistently performed better than 6.2, and if we consider the data point for 1000 items an outlier, all values for Sitecore 6.3 are less than 40% of the 6.2 value. This shows considerable improvement. The next test was to access each of the child items in turn.
timer (find pwd)
At the top end Sitecore 6.3 performs at about 30% of the 6.2 values. The final test was to recycle the items.
timer (find rm)
No huge performance improvements here. One notable artefact about this test was that for both Sitecore 6.2 and 6.3 I wasn’t able to get a reliable test value for 2000 items. But I was only really interested in comparison between the 2 versions, so no big deal. Well I think the data speaks for itself. Sitecore 6.3 shows considerable performance gains over Sitecore 6.2. But is it ready to support “massive” amounts of content? I’m not too sure if I can answer that being that ”massive” is such a subjective term. Even if it’s not, Sitecore are definitely headed in the right direction with these performance gains. And now that you get better performance with large volumes of content in a single location keep in mind that just because you can do something, doesn’t mean you should :)
This is great info. On the query by name test, did you repeatedly get the odd behavior of 1,000 items being so much slower than expected from the tests at 500 items and 2,000 items?
Would you be willing to run several rounds of Sitecore 6.3 tests between 500 and 1,000 to see where the performance drop-off becomes evident?
Also, do points at 1,250 and 1,500 fall linearly between the 1,000-2,000 results or do they drop back down from the oddness at 1,000?
Again, great work from you and the Sitecore coding team.