10

In Sitecore 9 the event model has changed somewhat and events are now queued within each DB (master, core, web etc).

Edit: turns out I was a bit wrong here. Sitecore has for some time had the ability to queue events with each DB. However, Sitecore.Eventing.EventManager.QueueEvent() has been deprecated in Sitecore 9.

Previously in Sitecore, we would use code like this to raise an event:

var args = new Testing.Events.MyEventArgs();
Sitecore.Events.Event.RaiseEvent("testing:myevent", args);

Note how the name of the event is passed ("testing:myevent") and then some args which I believe are generic event args which are then cast back to a specific EventArg type within the handling event.

In Sitecore 9 and later, we would use something like below

var database = Sitecore.Configuration.Factory.GetDatabase("web");
var eventQueue = database.RemoteEvents.EventQueue;
eventQueue.QueueEvent(someObject);

I'm confused as to what/how to create the object someObject. The QueueEvent method has two signatures:

QueueEvent(RemoteEventArgs<TEvent> event)
QueueEvent(RemoteEventArgs<TEvent> event, bool addToGlobalQueue, bool addToLocalQueue)

Say I want to raise the event publish:end:remote to trigger a cache clear, I look at the configs and see a load of different handlers for this event (I have SXA and Commerce installed) but looking at the OOTB one which is Sitecore.Publishing.HtmlCacheClearer within Sitecore.Kernel.dll it has a method ClearCache(object sender, EventArgs e) i.e. just a generic EventArgs parameter.

I don't understand how the event object passed to QueueEvent identifies the name of the event. I also don't see how the event could be identified magically by passing in a specific event arg type.

4
  • 2
    You're confusing the two types of events. I really dislike that Sitecore chose to call them both "events". There are what I refer to as "local" events such as "publish:end:remote" and then there are true "remote" events, which are added and read from Event Queues. Also, this code has not changed. Every database has an event queue. Every server reads every relevant event from every event queue.
    – jrap
    Commented Oct 17, 2019 at 12:48
  • sitecore-community.github.io/docs/pipelines-and-events/events <- best guide to answer your question.
    – jrap
    Commented Oct 17, 2019 at 12:50
  • @jrap I did look at that already actually, but it mentions using Sitecore.Eventing.EventManager.QueueEvent which is marked as Obsolete in Sitecore 9. So I'm assuming that page has not been updated for Sitecore 9. I'll happily do that myself, once I've wrapped my head around what I'm meant to be doing! Commented Oct 17, 2019 at 13:06
  • Interesting, I did not notice that method was marked as obsolete. However, my answer should still be mostly correct. I'll review for Sitecore 9 though. Perhaps they no longer want you to leverage the systemDatabase and instead want you to queue events in your database of choice.
    – jrap
    Commented Oct 17, 2019 at 13:10

2 Answers 2

9

In Sitecore 9 the event model has changed somewhat and events are now queued within each DB (master, core, web etc).

This is partially correct. You could always queue remote events in your Sitecore database of choice. Sitecore 9 still has a concept of a preferred event queue database.

Two options for queueing a remote event

  1. Sitecore.Eventing.EventManager.QueueEvent<T>(remoteEvent); (this is now obsolete in Sitecore 9, see the next section explaining the preferred approach)
    1. This inserts a remote event into the EventQueue table defined in configs: <add name="sitecore" type="Sitecore.Eventing.EventProvider, Sitecore.Kernel" systemDatabaseName="core"/>
  2. Sitecore.Configuration.Factory.GetDatabase("web").RemoteEvents.EventQueue.QueueEvent(remoteEvent);
    1. Choose your database. This can help reduce the number of records in the systemDatabase. Also helpful if your remote event is tied to a specific database, such as web.

Sitecore 9 Updates For Option 1 Above

In Sitecore 9, if you wish to leverage the defaultEventQueue for your event, you need to rely on DI to help you out. Use the ServiceLocator pattern or Constructor Injection. Example:

public class MyController{
  private readonly IEventQueue _eventQueue;

  public MyController(IEventQueue eventQueue)
  {
    _eventQueue = eventQueue;
  }

  public void MyMethod(){
    _eventQueue.QueueEvent<MyType>(myTypeInstance); // plus overload for global and local declaration
  }
}

You are able to swap out the provider to an Async version via ~\App_Config\Include\Examples\Sitecore.Publishing.EventQueueProvider.Async.config.example without modifying your code.

It is also worth mentioning that 9.2 contains modifications to the defaultEventQueue config entries:

  <eventing>
    <!-- EVENT QUEUE -->
    <eventQueueProvider defaultEventQueue="core">
      <eventQueue name="web" type="Sitecore.Data.Eventing.$(database)EventQueue, Sitecore.Kernel">
        <param ref="dataApis/dataApi[@name='$(database)']" param1="$(name)" />
        <param ref="PropertyStoreProvider/store[@name='$(name)']" />
      </eventQueue>
      <eventQueue name="master" type="Sitecore.Data.Eventing.$(database)EventQueue, Sitecore.Kernel" role:require="!ContentDelivery">
        <param ref="dataApis/dataApi[@name='$(database)']" param1="$(name)" />
        <param hint="" ref="PropertyStoreProvider/store[@name='$(name)']" />
      </eventQueue>
      <eventQueue name="core" type="Sitecore.Data.Eventing.$(database)EventQueue, Sitecore.Kernel" role:require="!ContentDelivery">
        <param ref="dataApis/dataApi[@name='$(database)']" param1="$(name)" />
        <param ref="PropertyStoreProvider/store[@name='$(name)']" />
      </eventQueue>
    </eventQueueProvider>
    <eventQueueProvider role:require="ContentManagement or ContentDelivery">
      <patch:attribute name="defaultEventQueue">web</patch:attribute>
    </eventQueueProvider>
  </eventing>

Notice the inclusion of ContentManagement or ContentDelivery. This change is absent in 9.0-9.1 as evidenced by @Gatogordo's post: https://ggullentops.blogspot.com/2019/03/eventqueue-in-sitecore-91.html


var args = new Testing.Events.MyEventArgs();

Sitecore.Events.Event.RaiseEvent("testing:myevent", args);

This code is targeting a "local" event, such as "publish:end:remote". It's intrinsically decoupled from the previous code which queue's remote events. Raising an event specifically targets a local event specified in the config on the executing server.

The reason the two are often discussed in unison is that many remote events call a local event during execution.


I'm confused as to what/how to create the object someObject. The QueueEvent method has two signatures:

QueueEvent(RemoteEventArgs event)

QueueEvent(RemoteEventArgs event, bool addToGlobalQueue, bool addToLocalQueue)

someObject can be any class that is serializable, literally. Any given Event Queue can hold 2GB of serializable data per record. Obviously, don't do this, but it's possible.


I don't understand how the event object passed to QueueEvent identifies the name of the event. I also don't see how the event could be identified magically by passing in a specific event arg type.

Remote events are subscribed to, typically upon initialization. The subscription targets the type of the object. As previously mentioned, make sure your type is serializable (with DataContract, DataMember, etc.), and then add a subscription to target that type.

To see this for yourself, review this config entry in the <initialize> pipeline and decompile the source:

  <processor type="Sitecore.Eventing.Remote.RemoteEventMap, Sitecore.Kernel" method="InitializeFromPipeline">
    <param desc="eventQueue" ref="eventing/eventQueueProvider/eventQueue[@name='core']" role:require="(Standalone or Reporting or Processing) and !ContentManagement and !ContentDelivery" />
    <param desc="eventQueue" ref="eventing/eventQueueProvider/eventQueue[@name='web']" role:require="ContentManagement or ContentDelivery" />
  </processor>

When the event queue's are read (by default every 2 seconds), each server will pull the latest unread events applicable to their server, then compare the data types with the known types in their subscription map. If there is a match, it executes the method specified in the subscription.


I explain most of this in concise detail at my blog post: https://sitecorerap.wordpress.com/2017/12/29/simple-remote-events-in-sitecore/

3
  • 1
    Be careful with option 1 to queue a remote event as it might be raised by the CM in a database that is not known to your CD's
    – Gatogordo
    Commented Oct 17, 2019 at 14:29
  • Exactly, which is why the recommendation is for all servers in a cluster to share the same definition for systemDatabaseName, though it is not required if you know what you're doing.
    – jrap
    Commented Oct 17, 2019 at 15:52
  • 1
    @Gatogordo, see my updates. It appears Sitecore fixed this discrepancy in 9.2+
    – jrap
    Commented Oct 17, 2019 at 19:48
3

Can't say I've had to do this, but firing up my friend ILSpy, I've got a few ideas for you:

You can open Sitecore.Kernel and find IEventQueue and "analyze" the QueueEvent() method to find places it's used in real code.

That lets me find this code:

private Handle QueuePublish(DistributedPublishOptions[] options, Language clientLanguage)
{
    Handle handle = new Handle();
    AddPublishRegistration(new PublishRegistration(handle));
    defaultQueue.QueueEvent(new StartPublishingRemoteEvent(options, handle, Context.GetUserName(), clientLanguage), addToGlobalQueue: true, addToLocalQueue: true);
    return handle;
}

Which looks like it might be an answer to how you trigger a remote publish event for the beginning of a publish?

The end of a publish seems to be indicated by:

protected virtual void NotifyEnd()
{
    RemoveRecoveryDump();
    Event.RaiseEvent("publish:end", this);
    if (Options != null && Options.TargetDatabase != null)
    {
        Options.TargetDatabase.RemoteEvents.EventQueue.QueueEvent(new PublishEndRemoteEvent(this));
    }
}

So perhaps PublishEndRemoteEvent is what you need here?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.