Solving Transaction Timeouts with Salesforce Platform Events
Stop transaction timeouts in Salesforce by moving heavy downstream processes to Platform Events. Learn how to implement asynchronous pub-sub patterns with idempotency.
25 Apr 2026, 13:11 UTC

The Cost of Synchronous Integration
When a Salesforce record is saved, developers often trigger a chain of downstream actions: updating a billing system, notifying a shipping partner, and updating a data warehouse. If these are handled via synchronous Apex callouts or heavy processing, a single slow external API can cause the entire transaction to time out. This rolls back the core record save, frustrating the user and creating data inconsistency.
The solution is to move from a request-response model to an event-driven architecture using Platform Events. By publishing an event and returning control to the user immediately, you decouple the primary business process from the secondary side effects.
Understanding the Event Bus
Platform Events are custom objects with a __e suffix that function as messages on a publish-subscribe (pub-sub) bus. When you call EventBus.publish(), the message is placed on the bus, and the publisher continues its execution without waiting for a response.
There are two primary event types to consider:
- Immediate Events: These are designed for lower volumes and provide first-in-first-out (FIFO) delivery guarantees. Use these when the sequence of events is critical.
- High Volume Events: These are optimized for large bursts of data. They do not guarantee order but offer higher throughput and are the standard choice for most integration patterns.
Transaction Boundaries and Idempotency
A critical technical detail is that subscribers—such as Apex triggers on the event object—run in a separate transaction from the publisher. This means if the publisher's transaction fails and rolls back, the event may still have been published depending on the publish method used. Conversely, a failure in the subscriber trigger does not roll back the original record save.
Furthermore, Platform Events provide "at-least-once" delivery. This means a subscriber might receive the same event more than once due to network retries or replay requests. To prevent duplicate processing, subscribers must be idempotent. This is typically achieved by storing the unique Event ID or a business key (like an Order Number) in a custom object to check if the event has already been processed.
Worked Example: Decoupled Order Processing
In this scenario, we want to notify an external system when an Order is marked as 'Completed' without making the user wait for the external API response.
1. Event Definition
Create a Platform Event in Setup named Order_Completed__e. Add the following custom fields:
Order_Number__c(Text)Total_Amount__c(Number)
2. The Publisher
Run this logic within an Apex class or trigger. This code should be executed by a user with the "Create and Publish Platform Events" permission.
public class OrderService {
public static void markOrderComplete(Id orderId) {
// Update the record first
Order ord = [SELECT OrderNumber, TotalAmount FROM Order WHERE Id = :orderId];
ord.Status = 'Completed';
update ord;
// Define the event payload
Order_Completed__e event = new Order_Completed__e(
Order_Number__c = ord.OrderNumber,
Total_Amount__c = ord.TotalAmount
);
// Publish the event
List<Database.SaveResult> results = EventBus.publish(new List<Order_Completed__e>{event});
// Check for publishing errors
for (Database.SaveResult sr : results) {
if (!sr.isSuccess()) {
System.debug('Event publish failed: ' + sr.getErrors());
}
}
}
}Expected Check: The update ord statement commits, and the method returns immediately regardless of whether the subscriber has started processing.
3. The Subscriber
Create an Apex trigger on the Order_Completed__e object to handle the logic asynchronously.
trigger OrderCompletedTrigger on Order_Completed__e (after insert) {
for (Order_Completed__e event : Trigger.new) {
// Idempotency check: verify if this Order_Number was already processed
// Example: check a 'Processed_Event__c' custom object
System.debug('Processing completion for Order: ' + event.Order_Number__c);
// Perform external callout or secondary update here
}
}Verification: Use the Streaming Monitor in Salesforce Setup to observe events hitting the bus in real-time. For external clients, a CometD client can be used to subscribe to the channel and verify the payload.
Trade-offs and Constraints
While Platform Events solve the timeout problem, they introduce new constraints:
- Governor Limits: There are strict daily limits on the number of events published and delivered. Exceeding these can lead to events being dropped.
- No Direct Rollback: Since the subscriber is in a different transaction, you cannot use a standard database rollback to undo the publisher's action if the subscriber fails.
- Payload Size: Events are intended for notifications, not large data transfers. Keep payloads lean and pass IDs rather than full record sets.
Closing Action Plan
To implement this pattern effectively: first, define a strict schema for your __e object to act as a contract between systems. Second, implement a deduplication mechanism in your subscribers to handle at-least-once delivery. Finally, monitor your org's publishing limits via the System Overview page to ensure your event volume doesn't hit a ceiling during peak business hours.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.