Working with platform events can be complex due to their asynchronous nature. This post explores how to track event publishing with Apex using Universally Unique IDentifiers (UUIDs) and covers how to correlate events with their UUIDs using publish callbacks.

The challenge of event publishing

Platform event publishing is always asynchronous. As a result, if an error prevents the event from being published by the event bus, you may not immediately be notified about the failure.

For example, in Apex, the Database.SaveResult instance immediately returned by EventBus.publish is the result of enqueuing the event for publication on the event bus, not the actual publishing. This means that writing the following code is not enough to ensure successful event publishing; it will only catch some errors like missing required event field values:

The same logic holds true when creating an event with the REST API sObjects API endpoint or with Flow. You get an immediate response, but it reflects just the enqueuing of the event on the bus, not the actual publishing.

This raises the following question: How can you ensure that events are published and can you deal with errors? Since capturing the response from a call to EventBus.publish is not enough to guarantee a successful publication with Apex, Salesforce introduced asynchronous event publishing callbacks to help track event publishing.

diagram showing asynchronous publishing with Apex, REST API, and Flow and the Apex event publish callback

Tracking event publishing with an Apex callback and identifiers

Apex lets you track asynchronous event publishing with a callback mechanism that executes custom code. The logic in the callback can use event identifiers to correlate publishing operation results with the events that you enqueued for publications.

Every platform event is uniquely identified by a UUID string, such as 6ba5db7e-c27b-4a67-a3c5-cf425ffcaf53. Platform events hold their UUID in an EventUuid read-only field. This field is not visible in the metadata of the platform event, but is available in Apex and in the event envelope.

Implement an event publish callback

When using Apex event publish callbacks, you must implement one or both of two interfaces:

Handling platform events in a publish callback method that supports event correlation is a four-step process:

  1. Assemble a list of data objects holding event data. Some fields on the event payload might not be necessary for handling errors or republishing the event (for example: CreatedDate, CreatedById, ReplayId, and Id). While you could directly pass event instances to the callback, we recommend that you don’t pass all event fields to avoid hitting the callback handler’s internal limits. Instead, implement a data object (MyEventInfo in the example below) that holds the event UUID and the minimum amount of information required to identify your events and potentially republish them.
  2. Instantiate the event publish callback. Pass your data objects from the previous step to your callback constructor. This allows your callback class to store the important event data for later access. Behind the scenes, the data held by the callback instance is persisted to the database as the callback runs asynchronously in a separate transaction. The example shown below uses an eventMap property to associate event UUID strings with the MyEventInfo data objects for easy retrieval.
  3. Enqueue the events for publication. Call EventBus.publish with your events and the instance of your callback class.
  4. Handle the publish callback. Depending on the publish operation outcome, the onSuccess or onFailure method of your callback class gets called. You can then use the getEventUuids() method from the SuccessResult or FailureResult object to retrieve the list of event UUIDs. Once you have the UUIDs, you can then correlate them with the information that is stored in eventMap.

class diagram showing the relationship between a publisher class and a callback class that implements two interfaces (EventBus.EventPublishFailureCallback and EventBus.EventPublishSuccessCallback)

Check out the PlatformEventRecipes.publishEventWithCallbacks recipe from the Apex Recipes sample for a complete example with its source code.

Obtain UUIDs for your events

When you work with a platform event publish callback and you want to handle event publishing failures or success with event correlation, you need to obtain the event UUID before publishing it. However, there’s an important catch: You can’t use the event object constructor (new Event__e() for instance) as it doesn’t initialize the EventUuid field. Additionally, you can’t assign EventUuid manually later since the field is read-only.

If you use the event object constructor, you only get a value for EventUuid after calling EventBus.publish. Unfortunately, this happens too late for working with a publish callback and event correlation: The callback will not have access to the UUID as it’s not yet initialized.

Instead of using the event object constructor, you must use the sObjectType.newSObject method to create an instance of your event. This initializes the EventUuid field value ahead of the call to EventBus.publish and as a result, you can pass the event UUIDs in your callback constructor.

Handle user context changes

The call to publish an event and the callback logic run in two different transactions. Make sure to pay attention to the change of user context between the method that calls EventBus.publish and the onSuccess or onFailure callback methods. The first one can run with any user, including the system, whereas the callback methods always run with the Automated Process user because of its asynchronous nature.

If you need to keep information about the user context, such as who requested the event publication, consider passing this information in the callback constructor as this is not part of the event data (there is no “created by” field in platform events).

Conclusion

Now that you know more about how to track event publishing with Apex using asynchronous publish callbacks — and some of the gotchas that you may face, including working with event UUIDs and user context switches during the publish and callback operations — I encourage you to explore the Apex Recipes sample app for some code examples.

Resources

About the author

Philippe Ozil is a Principal Developer Advocate at Salesforce where he focuses on the Salesforce Platform. He writes technical content and speaks frequently at conferences. He is a full-stack developer and enjoys working with APIs, DevOps, robotics, and VR projects. Follow him on X @PhilippeOzil, on LinkedIn @PhilippeOzil, or check his GitHub projects @pozil.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS