Wednesday, October 16, 2013

Slow loading, locking and unlocking of items in "My Items" when contents are enormous (800k++ items)

Few weeks back I was engaged in solving an issue with slow loading, locking and unlocking of items in "My Items" when contents in Sitecore content tree are massive in term of numbers. 

Being in a print media space, the client who also happened to be one of the leading print media company in Malaysia, they naturally would have massive amounts of contents. Henceforth the performance of the locking mechanism through "My Item" interface was compromised.

To start of, here are the statistic before the tweak:

Number of items in the content tree: 800k++
Load "My Items"                            : 65 seconds
Unlock All "My Items"                    : 60 seconds
Unlock Single Item                         : 5 seconds

The problem or cause of this issue is because of the query being used to populate "My Items"

in 

namespace Sitecore.Shell.Applications.WebEdit.Dialogs.LockedItems
...
///
/// Raises the event.
/// 
///
/// The object that contains the event data.
///            
protected override void OnLoad(EventArgs e)
{
      Assert.CanRunApplication("/sitecore/content/Applications/Content Editor/Ribbons/Chunks/Locks/My Items");
      Assert.ArgumentNotNull((object) e, "e");
      base.OnLoad(e);
      if (XamlControl.AjaxScriptManager.IsEvent)
        return;
      ComponentArtGridHandler.Manage(this.Items, (IGridSource) new GridSource((IEnumerable) (Client.ContentDatabase.SelectItems("fast://*[@__lock='%\"" + Context.User.Name + "\"%']") ?? new Item[0])), true);
      CommonExtensions.LocalizeGrid(this.Items);
}

To solve this issue I drafted a list of logic to address this issue with minimal impact to the existing code and logic.

1. Store user locked item IDs into User Profile
2. Instead of querying the DB, the "LockedItemPage" a.k.a "My Items" will now first look at User Profile
3. If the User Profile is empty (string.empty), it will then call the query code and store this list of item IDs back to User Profile. So it is as expected for the first time user to be slow at first when clicking "My Items"
4. If indeed there is no items that are locked by the context user, an indicative string "Empty" to label the user having no items locked to him will be stored in his/her User Profile. This is to prevent the query to be executed unnecessary
5. Lock and Unlock mechanism will need to be updated to update the Locked Item IDs in the user profile
6. Prior to loading the list of items in "My Items", filtration of IDs for invalid IDs due to Admin unlocking and/or items being deleted are performed.

With that in mind, the code changes are made on the following commands and shell file

1. Item:CheckIn command
2. Item:CheckOut command
3. Webedit:UnlockAll command
4. sitecore\shell\applications\webedit\dialogs\lockeditems\LockedItems.xaml.xml
5. Create a class that will override the backend code for LockedItems.xaml
6. Update the commands.config
7. Create a User Profile in Core DB

Instead of going through the code as it is quite extensive, I will instead share the code to achieve the above. Here is the link https://www.dropbox.com/s/qnsoue98dfaerjw/srcv3.zip 

The link will contains only the code in which it will need to be compile and also user profile to be created before the above will work.

After implementing the above, the new statistic is as follow:

Load "My Items"                            : 2seconds
Unlock All "My Items"                    : 7 seconds
Unlock Single Item                         : 1 seconds