What is the Platform Event Trap?

platform event trap

The Platform Event Trap is a design anti-pattern on the Salesforce platform that undermines the asynchronous and decoupled nature of Platform Events. It leads to a system that is: – Complex and hard to debug. – Has the potential for infinite loops. – Has performance and governor limit concerns. – Has effectively recreated the problems of synchronous processing but with even more overhead. It occurs when a listener (a trigger or Apex Subscriber) to a Platform Event takes an action that directly or indirectly causes the same Platform Event to be published again, creating a feedback loop.

How does the trap spring?

The classic narrative here is a before and after state published as an event.

A typical workflow that may trap:

Object A changes.

A trigger on Object A fires and publishes a platform event trap (PE1). The payload contained the new state of Object A.

A subscription (perhaps an apex trigger on the platform event trap object) listens to PE1.

In the subscription logic, some business process runs, often resulting in another object (Object B) being updated or even Object A again.

A trigger on Object B fires (or the same trigger on Object A fires again).

Seeing the changes, a new platform event (PE2) is published. PE2 is often the same structure and content as PE1 or close enough that the previous subscriber will process it.

The subscriber listens for PE2 and runs the same logic, again going back to step 4.

This will create a publish-subscribe loop until a governor limit is reached (e.g. maximum executed transaction) crashing that process.

Visual Presentation of the Trap

graph TD
    A[Object A is Updated] --> B[Object A Trigger Fires];
    B --> C[Publish Platform Event PE1];
    C --> D[PE1 Subscriber Logic Runs];
    D --> E[Update Object B];
    E --> F[Object B Trigger Fires];
    F --> G[Publish Platform Event PE2];
    G --> H[PE2 Subscriber Logic Runs];
    H --> I[Update Object A or another object...];
    I --> B;  %% This is the loop! %%

    style C fill:#ff9999;
    style G fill:#ff9999;

A Real-World Sample

Think about a process where the Last_Activity_Date__c on an Account gets updated every time a related Contact is updated.

Contact Trigger (After Update): It publishes a ContactUpdatedEvent. The payload contains the AccountId for the Contact.

ContactUpdatedEvent Subscriber: The Apex class serves as the subscriber to the event. It updates the Last_Activity_Date__c on the related Account to DateTime.now().

Account Trigger (After Update): This trigger is also configured to publish an AccountUpdatedEvent each time an Account is updated, for other automated processes (e.g., syncing to an external system).

  • The Trap is Set.
  • You update a Contact.
  • The Contact trigger publishes a ContactUpdatedEvent.
  • The subscriber updates the related Account.
  • Account trigger fires and it publishes the AccountUpdatedEvent.
  • If any subscriber of the AccountUpdatedEvent logic updates a Contact, the flow starts again.

How to Avoid the Platform Event Trap

Avoiding the trap takes thoughtful planning and discipline. Here are the primary strategies:

1. Use a “Circuit Breaker” Pattern

This is the highest level of protection. The idea here is to pass context on your event payload, letting a subscriber know whether they want to process this event or ignore it.

Causation ID: Include a CausationId field that uniquely identifies your event payload. When publishing an event, check if you’re already processing another event of that type for a given transaction chain. If so, do not publish a new event.

Event Source: Include an EventSource field. For example: If the Account trigger invokes a publish event because the Account was updated via a Contact, it could set EventSource = ‘ContactSyncProcess’. The subscriber for the Contact sync would see this EventSource and know to skip the processing logic to avoid the loop.

Example payload with circuit breaker:

json

// Platform Event Payload
{
    "RecordId": "001xx000003DGRE",
    "FieldName": "Last_Activity_Date__c",
    "NewValue": "2023-10-27T12:00:00Z",
    "CausationId": "a1Bxx0000000001", // A unique ID for this transaction chain
    "EventSource": "ContactActivityTracker" // Who published this event
}

2. Design “Smarter” Subscribers

Your Apex subscription logic should be idempotent and aware of context.

Idempotence: The Apex logic should be safe to execute twice with the same or similar event data, and not cause duplicate side effects or errors.

State Checking: Before taking an action (such as an update), check whether you absolutely have to do it. If the Account’s Last_Activity_Date__c is already the value you’re about to set, it is probably a waste to perform a DML operation.

3. Strategic Trigger and Automation Planning

Stay on top of the entire automation chain.

Don’t Publish Events from Standard Object Triggers: Publish your events from a different and more controlled location in the process- either from asynchronous Apex or from the Subscriber after it has done some logic.

Track Dependencies: Create and clearly identify all the automations (including workflows, flows, and triggers) and event publications/subscriptions so that you can see where potential loops could occur before they actually do in production.

Final Thoughts

The Platform Event Trap isn’t some shortcoming of the Platform Events technology. It’s simply a bad usage of the tool- a good case of a powerful tool that is difficult to design around.

You can leverage the power of Platform Events to create a scalable, decoupled integration approach, only when understanding that you could trigger an infinite loop, as well as taking into account the Circuit Breaker to help mitigate the risk of performance issues.

Leave a Reply

Your email address will not be published. Required fields are marked *