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.
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:
-
EventBus.EventPublishFailureCallback
, which comes with avoid onFailure(EventBus.FailureResult result)
method that handles event publishing failures. -
EventBus.EventPublishSuccessCallback
, which comes with avoid onSuccess(EventBus.SuccessResult result)
method that handles successful event publishing.
Handling platform events in a publish callback method that supports event correlation is a four-step process:
- 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,
andId
). 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. - 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 theMyEventInfo
data objects for easy retrieval. - Enqueue the events for publication. Call
EventBus.publish
with your events and the instance of your callback class. - Handle the publish callback. Depending on the publish operation outcome, the
onSuccess
oronFailure
method of your callback class gets called. You can then use thegetEventUuids()
method from theSuccessResult
orFailureResult
object to retrieve the list of event UUIDs. Once you have the UUIDs, you can then correlate them with the information that is stored ineventMap
.
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
- Platform event recipes from the Apex Recipes sample app
- Platform Events Developer Guide
- Apex Reference Guide
- Trailhead
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.