SfSkills Domains Docs GitHub

Home / integration / platform-event-publish-patterns

platform-event-publish-patterns

Publishing Platform Events: EventBus.publish, PublishBehavior (PublishImmediately vs PublishAfterCommit), high-volume events, event allocation, publish failures, Change Data Capture comparison. NOT for consuming events in Apex — use apex/platform-events-apex. NOT for CDC events — use integration/change-data-capture-integration.

Open the skill folder on GitHub


Platform Event Publish Patterns

Activate when emitting Platform Events from Apex, Flow, or external callers. Publish semantics have subtle implications: PublishAfterCommit vs PublishImmediately determines whether rollback erases the event, high-volume events consume monthly allocation, and publish failures surface differently from synchronous DML errors.

Before Starting

Core Concepts

EventBus.publish

MyEvent__e evt = new MyEvent__e(Order_Id__c = oid, Amount__c = amt);
Database.SaveResult sr = EventBus.publish(evt);
if (!sr.isSuccess()) {
    for (Database.Error err : sr.getErrors()) {
        System.debug(err.getMessage());
    }
}

Returns a Database.SaveResult per event, or List<SaveResult> for bulk.

PublishBehavior

Set on event metadata (PublishBehavior: PublishAfterCommit | PublishImmediately):

ReplayId and durability

Every published event is stored with a ReplayId; a subscriber can resume from a stored ReplayId, or use -1 (default — new events only) or -2 (replay everything still retained). Retention is 72 hours for high-volume events — which is every event definable since Spring '19 — and 24 hours for legacy standard-volume events, retiring in Winter '27. Retention is fixed: no field or setting extends it, so a replay window longer than 72 hours must be served by a durable copy the publisher writes alongside the event.

Event allocation

Publishing is metered per hour, org-wide: 250,000/hour for high-volume events on Enterprise, Performance, and Unlimited; 50,000/hour on Developer; add-on capacity in +25,000/hour increments. Bulk publishes consume one unit per event published, so a 10,000-record batch that publishes one event per record spends 10,000 of the hourly allocation — batching domain changes into coarser events is the lever when a design approaches the ceiling. (Event delivery to CometD/empApi subscribers is metered separately on a 24-hour basis; do not conflate the two.)

Publishing from async contexts

Triggers, Queueables, Batch, Scheduled — all can publish. Publishing from a future method works but adds complexity; prefer publishing in the originating transaction.

Common Patterns

Pattern: Publish after DML commit

insert orders;
List<Order_Created__e> evts = new List<Order_Created__e>();
for (Order__c o : orders) evts.add(new Order_Created__e(Order_Id__c = o.Id));
EventBus.publish(evts);  // fires after commit

Pattern: Bulk publish with result check

List<SaveResult> results = EventBus.publish(events);
List<Id> failed = new List<Id>();
for (Integer i = 0; i < results.size(); i++) {
    if (!results[i].isSuccess()) failed.add(events[i].someId__c);
}

Pattern: Retry via Queueable

If SaveResult failure includes "STORAGE_LIMIT_EXCEEDED" or similar transient error, re-enqueue via Queueable with exponential backoff.

Decision Guidance

ScenarioPublishBehavior
Business-logic event — must not fire on rollbackPublishAfterCommit
Fire-and-forget telemetryPublishImmediately
Chain to external system, must commit firstPublishAfterCommit
Alert/audit event, okay if tx rolled backPublishImmediately
  1. Decide PublishBehavior on the event metadata.
  2. Publish via EventBus.publish(event) or bulk list.
  3. Always inspect SaveResult — publish is not guaranteed.
  4. For business-critical events, pair with an outbox pattern (write to a custom object, async publisher retries on failure).
  5. Monitor monthly allocation via Setup → Platform Events.
  6. Test with Test.startTest() / Test.getEventBus().deliver() to drive subscribers.
  7. Document event schema, versioning strategy, and retry guarantees.

Review Checklist

Salesforce-Specific Gotchas

  1. PublishAfterCommit events do NOT fire in test context without Test.getEventBus().deliver(). Tests silently pass while real subscribers wouldn't be invoked.
  2. Platform Events cannot be published from a transaction that's rolled back via Database.rollback when behavior is PublishAfterCommit — no event fires. When PublishImmediately, the event fires even on rollback.
  3. Bulk publish allocates one event per record. 200 events = 200 allocation units.

Output Artifacts

ArtifactDescription
Event metadata specPublishBehavior + field list
Publisher Apex classBulk publish + SaveResult handling
Outbox pattern (optional)Custom object + retry publisher
Monitoring dashboardAllocation + failure metrics

Package files