Platform Events: How Timing Affects Apex Trigger Batching

Nagendra SinghNagendra Singh
4 min read

Introduction

Have you ever wondered why your Platform Event trigger sometimes processes multiple events at once, and other times it handles them one by one? If you've worked with Platform Events in Salesforce, you've probably noticed this intriguing behaviour and been curious about the "why" behind it!

The Mystery of Event Batching

When multiple platform events are published very close together, the Apex Platform Event (PE) trigger often receives them as one batch. If there's a larger time gap between publishes, the trigger tends to receive separate batches.

While Salesforce doesn't document a precise millisecond window for this batching behaviour, understanding how it works is crucial for building robust event-driven architectures.

Why Platform Event Batching Matters

Platform Event triggers don't just execute randomly—they follow Salesforce's batch processing model. Here's what you need to know:

The Batch Reality

Your trigger handler runs against Trigger.new, which can contain multiple event messages simultaneously. By default, this can be up to 2,000 records at once (configurable via PlatformEventSubscriberConfig).

The Risks

When the platform groups messages into batches, you face several challenges:

  • SOQL/DML Governor Limit Explosions: Processing 2,000 events might exceed your limits

  • CPU Timeout Issues: Bulk processing can be computationally intensive

  • Race Conditions: Concurrent processing of related data can cause conflicts

  • Memory Pressure: Large batches consume more heap space

Real-World Impact

Imagine you're processing order updates. If 50 orders update simultaneously, or very close to one another from multiple different processes, your trigger might:

  • Batched scenario: Process all 50 in one efficient transaction

  • Individual scenario: Create 50 separate transactions, potentially hitting daily limits

Interactive Demo: Seeing Batching in Action

Let me show you exactly how this batching behaviour works with a practical demonstration:

Code Implementation: Two Scenarios

I've created a Lightning Web Component to test different publishing patterns and observe the batching behaviour:

  1. Scenario 1: Rapid-Fire Publishing

    Goal: Publish 10 events consecutively in a tight loop

     @AuraEnabled
     public static String publishRapidEvents(Integer numberOfEvents) {
         try {
             List<LogEvent__e> events = new List<LogEvent__e>();
             List<Database.SaveResult> results = new List<Database.SaveResult>();
    
             for (Integer i = 1; i <= numberOfEvents; i++) {
                 LogEvent__e event = new LogEvent__e(
                         Message__c = 'Rapid Event',
                         EventNumber__c = i,
                         PublishTime__c = DateTime.now()
                 );
                 // Publish events in loop
                 results.add(EventBus.publish(event));
             }
    
             Integer successCount = 0;
             for (Database.SaveResult result : results) {
                 if (result.isSuccess()) {
                     successCount++;
                 }
             }
    
             return successCount + ' events published successfully';
         } catch (Exception e) {
             throw new AuraHandledException('Error publishing events: ' + e.getMessage());
         }
     }
    

    Expected Result: All events likely processed in one or few batches due to rapid succession.

  2. Scenario 2: Delayed Publishing

    Goal: Publish 3-4 events with intentional delays between each

     @AuraEnabled
     public static String publishDelayedEvents(Integer numberOfEvents, Integer delaySeconds) {
         try {
             Integer successCount = 0;
    
             for (Integer i = 1; i <= numberOfEvents; i++) {
                 LogEvent__e event = new LogEvent__e(
                         Message__c = 'Delayed Event ' + i,
                         EventNumber__c = i,
                         PublishTime__c = DateTime.now()
                 );
    
                 // Publish events in loop
                 Database.SaveResult result = EventBus.publish(event);
                 if (result.isSuccess()) {
                     successCount++;
                 }
    
                 // Add delay between events (simplified version)
                 if (i < numberOfEvents && delaySeconds > 0) {
                     Long startTime = Datetime.now().getTime();
                     Long endTime = startTime + (delaySeconds * 1000);
    
                     // Warning: This is CPU-intensive and for demo only
                     while (Datetime.now().getTime() < endTime &&
                             (Datetime.now().getTime() - startTime) < 10000) {
                         // Wait (max 10 seconds to avoid CPU limits)
                     }
                 }
             }
    
             return successCount + ' delayed events published';
         } catch (Exception e) {
             throw new AuraHandledException('Error publishing delayed events: ' + e.getMessage());
         }
     }
    

    Expected Result: Events likely processed in separate batches due to time gaps.

    ⚠️ Production Note: The delay implementation above uses a CPU-intensive loop for demonstration purposes only. In production, use proper asynchronous patterns or external scheduling.

Live Results

Here's what happened when I tested both scenarios:

Key Takeaways

Understanding Platform Event batching helps you:

  1. Optimize Performance: Design triggers that handle batches efficiently

  2. Prevent Governor Limit Issues: Avoid exceeding Salesforce limits

  3. Improve Reliability: Handle race conditions and concurrency properly

  4. Scale Better: Build event-driven solutions that perform well under load

0
Subscribe to my newsletter

Read articles from Nagendra Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nagendra Singh
Nagendra Singh

Allow me to introduce myself, the Salesforce Technical Architect who's got more game than a seasoned poker player! With a decade of experience under my belt, I've been designing tailor-made solutions that drive business growth like a rocket launching into space. 🚀 When it comes to programming languages like JavaScript and Python, I wield them like a skilled chef with a set of knives, slicing and dicing my way to seamless integrations and robust applications. 🍽️ As a fervent advocate for automation, I've whipped up efficient DevOps pipelines with Jenkins, and even crafted a deployment app using AngularJS that's as sleek as a luxury sports car. 🏎️ Not one to rest on my laurels, I keep my finger on the pulse of industry trends, share my wisdom on technical blogs, and actively participate in the Salesforce Stackexchange community. In fact, this year I've climbed my way to the top 3% of the rankings! 🧗‍♂️ So, here's to me – your humor-loving, ultra-professional Salesforce Technical Architect! 🥳