Hacking Sitecore and Life one pipeline at a time!

Working with ongoing dispatches

In the previous post, we looked at a way to resume a paused email campaign dispatch. The opposite is also possible i.e. pausing an ongoing dispatch, or just retrieve information about an ongoing dispatch.

When an email campaign dispatch starts, EXM uses the Sitecore.EmailCampaign.Cm.Dispatch.IMessageTaskRunner to run the actual dispatch. The implementation of this, by default Sitecore.EmailCampaign.Cm.Dispatch.MessageTaskRunner, is responsible for running all email campaigns currently being dispatched – according to the settings on EXM dispatch and performance.

Using the IMessageTaskRunner, we can get access to any running message tasks:

IEnumerable<DispatchTask> runningTasks = _messageTaskRunner.GetRunningTasks().OfType<DispatchTask>();

The DispatchTask gives us access to the email campaign that it is processing, as well as the number of contacts that has been processed e.g.

MessageItem messageItem = dispatchTask.Message;
int processedContacts = dispatchTask.Processed;

We would use this information to pause the dispatch after having processed a certain number of contacts, for example in cases were you are rate limited to only being allowed to send a certain number of emails per hour. However, the “Processed” property on the DispatchTask only returns the number of contacts processed by that specific DispatchTask, so if you have one/more dedicated dispatch servers, you will need to determine the number of processed contacts in another way. We can do this using the EcmDataProvider:

// Gets the campaign
EmailCampaignsData campaign = _dataProvider.GetCampaign(dispatchTask.Message.MessageId);

// Gets the total number of recipients
var totalRecipients = campaign.TotalRecipients;

// Gets the number of contacts remaninging in the dispatch queue
var recipientsInDispatchQueue = _dataProvider.CountRecipientsInDispatchQueue(dispatchTask.Message.MessageId, RecipientQueue.Recipient);

var numberOfProcessedContacts = totalRecipients - recipientsInDispatchQueue;

Using IEmailDispatch, we can then go ahead and for example pause the dispatch.

_application.EmailDispatch.Pause(dispatchTask.Message.MessageId);

Using IEmailDispatch ensures that for example all dedicated dispatch servers are notified that dispatch should pause.

This can all be put together e.g. to a Sitecore task. If doing so, the task should only be running on the primary CM and not on any dedicated dispatch servers, as the dedicated dispatch servers cannot notify other dedicated dispatch servers (or the primary CM) to pause.

using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Sitecore.Data.Items;
using Sitecore.DependencyInjection;
using Sitecore.EmailCampaign.Cm.Dispatch;
using Sitecore.EmailCampaign.Model.Dispatch;
using Sitecore.EmailCampaign.Model.Message;
using Sitecore.Framework.Conditions;
using Sitecore.Modules.EmailCampaign.Application;
using Sitecore.Modules.EmailCampaign.Core.Data;
using Sitecore.Tasks;

namespace Sitecore.Hacker
{
  [UsedImplicitly]
  public class PauseOngoing
  {
    private readonly EcmDataProvider _dataProvider;
    private readonly IApplication _application;
    private readonly IMessageTaskRunner _messageTaskRunner;

    public PauseOngoing()
      : this(ServiceLocator.ServiceProvider.GetService<EcmDataProvider>(), Application.Instance, ServiceLocator.ServiceProvider.GetService<IMessageTaskRunner>())
    {
    }

    internal PauseOngoing([NotNull] EcmDataProvider dataProvider, [NotNull] IApplication application, [NotNull] IMessageTaskRunner messageTaskRunner)
    {
      Condition.Requires(dataProvider, nameof(dataProvider)).IsNotNull();
      Condition.Requires(application, nameof(application)).IsNotNull();
      Condition.Requires(messageTaskRunner, nameof(messageTaskRunner)).IsNotNull();

      _dataProvider = dataProvider;
      _application = application;
      _messageTaskRunner = messageTaskRunner;
    }

    /// <summary>
    /// Executes task.
    /// </summary>
    /// <param name="itemArray">The item array.</param>
    /// <param name="commandItem">The <see cref="CommandItem"/>.</param>
    /// <param name="scheduledItem">The <see cref="ScheduleItem"/>.</param>
    public void Execute(Item[] itemArray, CommandItem commandItem, ScheduleItem scheduledItem)
    {
      IEnumerable<DispatchTask> runningTasks = _messageTaskRunner.GetRunningTasks().OfType<DispatchTask>();
      
      foreach (DispatchTask dispatchTask in runningTasks)
      {
        EmailCampaignsData campaign = _dataProvider.GetCampaign(dispatchTask.Message.MessageId);
        var totalRecipients = campaign.TotalRecipients;
        var recipientsInDispatchQueue = _dataProvider.CountRecipientsInDispatchQueue(dispatchTask.Message.MessageId, RecipientQueue.Recipient);
        var numberOfProcessedContacts = totalRecipients - recipientsInDispatchQueue;

        if (numberOfProcessedContacts > 500)
        {
          _application.EmailDispatch.Pause(dispatchTask.Message.MessageId);
        }
      }
    }
  }
}

Categorised in: EXM, Sitecore

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 1,237 other subscribers

Blog Stats

  • 138,917 hits
Follow Sitecore Hacker on WordPress.com
Sitecore® and Own the Experience® are registered trademarks of Sitecore Corporation A/S in the U.S. and other countries.  This website is independent of Sitecore Corporation, and is not affiliated with or sponsored by Sitecore Corporation.