Home » 2014 » February

Monthly Archives: February 2014

Periodically Rebuild Link Databases using an Agent in Sitecore

Last week a colleague had asked me whether rebuilding the Link Database would solve an issue she was seeing. That conversation got me thinking: wouldn’t it be nice if we could automate the rebuilding of the Link Database for each Sitecore database at a scheduled time?

I am certain others have already created solutions to do this — if you know of any, please share in a comment — but I didn’t conduct a search to find any (I normally advocate not reinventing the wheel for code solutions but wanted to have some fun building a new solution).

In the spirit of my post on putting Sitecore to work for you, I built the following Sitecore agent (check out John West’s blog post on Sitecore agents to learn more):

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;

using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Diagnostics;
using Sitecore.Jobs;

namespace Sitecore.Sandbox.Tasks
{
    public class RebuildLinkDatabasesAgent
    {
        private static readonly IList<Database> Databases = new List<Database>();
        private static readonly Stopwatch Stopwatch = Stopwatch.StartNew();

        public void Run()
        {
            JobManager.Start(CreateNewJobOptions());
        }

        protected virtual JobOptions CreateNewJobOptions()
        {
            return new JobOptions("RebuildLinkDatabasesAgent", "index", Context.Site.Name, this, "RebuildLinkDatabases");
        }

        protected virtual void RebuildLinkDatabases()
        {
            Job job = Context.Job;
            try
            {
                RebuildLinkDatabases(Databases);
            }
            catch (Exception ex)
            {
                job.Status.Failed = true;
                job.Status.Messages.Add(ex.ToString());
            }

            job.Status.State = JobState.Finished;
        }

        private void RebuildLinkDatabases(IEnumerable<Database> databases)
        {
            Assert.ArgumentNotNull(databases, "databases");
            foreach (Database database in databases)
            {
                Stopwatch.Start();
                RebuildLinkDatabase(database);
                Stopwatch.Stop();
                LogEntry(database, Stopwatch.Elapsed.Milliseconds);
            }
        }

        protected virtual void RebuildLinkDatabase(Database database)
        {
            Assert.ArgumentNotNull(database, "database");
            Globals.LinkDatabase.Rebuild(database);
        }

        protected virtual void LogEntry(Database database, int elapsedMilliseconds)
        {
            Assert.ArgumentNotNull(database, "database");
            if (string.IsNullOrWhiteSpace(LogEntryFormat))
            {
                return;
            }

            Log.Info(string.Format(LogEntryFormat, database.Name, elapsedMilliseconds), this);
        }

        private static void AddDatabase(XmlNode configNode)
        {
            if (configNode == null || string.IsNullOrWhiteSpace(configNode.InnerText))
            {
                return;
            }

            Database database = TryGetDatabase(configNode.InnerText);
            if (database != null)
            {
                Databases.Add(database);
            }
        }

        private static Database TryGetDatabase(string databaseName)
        {
            Assert.ArgumentNotNullOrEmpty(databaseName, "databaseName");
            try
            {
                return Factory.GetDatabase(databaseName);
            }
            catch (Exception ex)
            {
                Type agentType = typeof(RebuildLinkDatabasesAgent);
                Log.Error(agentType.ToString(), ex, agentType);
            }

            return null;
        }

        private string LogEntryFormat { get; set; }
    }
}

Logic in the class above reads in a list of databases set in a configuration file, adds them to a list for processing — these are only added to the list if they exist — and rebuilds the Link Database in each via a Sitecore job.

I added some timing logic to see how long it takes to rebuild each database, and capture this information in the Sitecore log.

I then wired up the above class in Sitecore using the following patch include configuration file:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <scheduling>
      <agent type="Sitecore.Sandbox.Tasks.RebuildLinkDatabasesAgent" method="Run" interval="00:01:00">
        <databases hint="raw:AddDatabase">
          <database>core</database>
          <database>master</database>
          <database>web</database>
        </databases>
        <LogEntryFormat>Rebuilt link database: {0} in {1} milliseconds.</LogEntryFormat>
      </agent>
    </scheduling>
  </sitecore>
</configuration>

I’ve set this agent to run every minute for testing, but it would probably be wise to have this run no more than once or twice a day.

After waiting a bit, I saw the following in my Sitecore log:

rebuilt-link-database

I do question the rebuild times. These seem quite small, especially when it takes a while to rebuild the Link Databases via the Sitecore Control Panel. If you have any ideas/thoughts on why there is an incongruence between the times in my log and how long it takes to rebuild these via the Sitecore Control Panel, please share in a comment.

Further, if you have any recommendations on making this code better, or have other ideas on automating the rebuilding of Link Databases in Sitecore, please drop a comment.

Until next time, have a Sitecoretastic day!