Sitecore Publishing Service Failed Publish Handling

If you have opted in to using Sitecore’s Publishing Service module to scale up your publishing operations you have probably come across situations where a particular publishing job fails behind the scenes. There are many reasons that a publish job can fail from incorrect configuration to issues related to the manifest calculation that the publishing service executes for every job.

The problem with this is that the failure will only really be detected if you happen to be looking at the publishing dashboard, which is often not the case for editors publishing content using workflow or automated publications that are triggered as part of a deployment pipeline.

So how can we detect and alert ourselves to a failed publish job? Well the publishing service comes with it’s own set of pipelines and event handlers defined in Sitecore.PublishingService.config (which is installed as part of the publishing service module package for Sitecore). The event to pay attention to is publishingservice:publishend. You will notice that the publishing service comes with a couple of out of the box handlers, one of which is responsible for raising “traditional” publish related events such as publish:end and publish:end:remote to ensure that code listening to these events continues to function. Go ahead and add a custom handler and patch it in after the out of the box job end handler:

patch:after=”*[@type=’Sitecore.Publishing.Service.Events.PublishingJobEndHandler, Sitecore.Publishing.Service’]” type=”YourClass, YourAssembly” method=”FailedPublishAlert”

Within your handler you need to reference libraries that are unfortunately not currently distributed via nuget so you will have to add local references for these by fetching copies from your local bin folder::

  • Sitecore.Publishing.Service.dll
  • Sitecore.Framework.Publishing.Abstractions.dll

You can also write to the publishing log with a bit of setup in the constructor which gives you access to the publishing service logger:


public class PublishingServiceEventHandler
{
    protected readonly IPublishingLog _logger;

    public PublishingServiceEventHandler() : this(
        new PublishingLogWrapper())
    {

    }

    public PublishingServiceEventHandler(IPublishingLog logger)
    {
        _logger = logger;
    }
}

In the FailedPublishAlert method the first job is to get access to the PublishingJobEndEventArgs:


protected PublishingJobEndEventArgs ExtractArgs(EventArgs args)
{
    var scArgs = args as SitecoreEventArgs;

    if (scArgs == null)
        return null;

    if (scArgs.Parameters == null || !scArgs.Parameters.Any() || !(scArgs.Parameters[0] is PublishingJobEndEventArgs))
        return null;

    return (PublishingJobEndEventArgs)scArgs.Parameters[0];
}

With access to the PublishingJobEndEventArgs EventData property you can check the status of the publish job which is an enumeration and then handle failure cases however you want to:


public void FailedPublishAlert(object sender, EventArgs args)
{
    var publishArgs = ExtractArgs(args);

    if (publishArgs == null)
        return;

    if (publishArgs.EventData.Status == PublishJobStatus.Failed)
    {
        HandleFailure(publishArgs);
    }
}

protected virtual void HandleFailure(PublishingJobEndEventArgs publishArgs)
{
    _logger.Info($"handling failed publish: {publishArgs.EventData.JobId}");
}

There are plenty of properties on the PublishingJobEndEventArgs EventData property that should allow you to do something meaningful in the event of a failure:

  • CompareRevisions
  • IncludeDescendants
  • ItemId
  • JobId
  • LanguageNames
  • MetaData
  • PublishDate
  • PublishType
  • RepublishAll
  • SourceDatabaseName
  • Status
  • Targets
  • Username

To wrap up, the publishing service certainly outperforms traditional publishing and is pretty much a must for solutions with large scale publishing requirements, however it is not without its problems (some would say that’s an understatement) and publish jobs can fail for many reasons. Fortunately the events and associated data are available so we can at least handle failure.

Happy publishing!

 

Advertisements

Sitecore Module: Profile Key Scaffolder

In the Sitecore Sunday Pt2: Programmatic Profile Association post I talked about Sitecore profiles that map 1 to 1 with existing site taxonomies and how to associate these profiles to your site visitors without having to re-tag all of your content.

As part of this process setting up profile, profile key, profile card and pattern card items is still required and this can be quite a time consuming task because for each profile key you have to:

  1. Create the profile key item
  2. Set its max value to 10 (or a scale of your choosing)
  3. Create a profile card item with the same name
  4. Set its radar graph to the maximum value for the profile key
  5. Repeat steps 3 & 4 for the pattern card

You can see with a lot of profile keys how this could become tedious.

Profile scaffolding to the rescue!

Download the Profile Scaffolder, which is a small and unobtrusive module (Helix stylee) that reduces the task outlined above to a single step:

  1. Create a profile key and give it a name. Done.

Happy profiling!

Sitecore Heartbeat Page & Web Forms for Marketers on Remote Servers

When you are running Sitecore with multiple load balanced content delivery servers, typically you would use Sitecore’s heartbeat page to monitor the health of each content delivery instance:

http://host/sitecore/service/heartbeat.aspx

This service does more than just return a http 200 status letting you know that the site is running, it also iterates over each connection string defined in ConnectionStrings.config and executes a simple SQL query against sys.tables to check for un-responsive databases.

This is all fine and dandy until you throw Web Forms for Marketers into the mix. On a remote installation of Web Forms for Marketers a connection string is added to ConnectionStrings.config that communicates with a content management instance and looks something like this:

Unfortunately the heart beat page will try to process this connection string but it doesn’t know what to do with it so the page subsequently returns a 500 server error every time.

Fortunately Sitecore provides a way around this. If you look at the standard web.config you will not find a setting called:

Sitecore.Services.Heartbeat.ExcludeConnection

But, the heart beat page is coded to check for it (expecting a pipe separated list) and by default excludes the connection string called “LocalSqlServer”.

As we know the best practice when adding custom settings to a Sitecore solution is to use include files. So, to fix this problem all we need to do is add (or update an existing) include file that contains the following:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <settings>
            
            <!-- stops the heartbeat page from returning an error 500 on delivery servers -->
            <setting name="Sitecore.Services.Heartbeat.ExcludeConnection" value="LocalSqlServer|remoteWfmService"/>

        </settings>
    </sitecore>
</configuration>

And that’s it! We have a working heart beat page on a content delivery instance that also has Web Forms for Marketers installed.