LinkedIn

Batch Apex - Better Exception Logging using Standard Platform Event - BatchApexErrorEvent

BatchApexErrorEvent is a standard platform event introduced by Salesforce as part of API version 44.0. This mainly helps us to get the details of unhandled exceptions that happened from Batch Apex.

To fire exception details to BatchApexErrorEvent, Batch Apex should implement  Database.RaisePlatformEvents interface.

Let us cover below in detail:


1. BatchApexErrorEvent Details

BatchApexErrorEvent holds below details:


Let us implement a batch apex and see how BatchApexErrorEvent is helpful for tracking unhandled exceptions.

2. Sample Batch Apex Scenario

Both Account and Contact has a field called Active__c which contains 2 values - Yes, No. When a parent Account is getting deactivated,ie Active=No, we need to mark all child contact's Active also as No.

Batch Apex Logic

Below Class is implemented to solve this.


You can see that this class has implemented Database.RaisePlatformEvents interface.

public with sharing class Batch_ContactDeactivation implements Database.Batchable<sObject>,Database.RaisesPlatformEvents {    

3. How to Subscribe to BatchApexErrorEvent

We can subscribe to this platform event in multiple ways like shown below:



4. Subscribe Using Flow

Let us subscribe to this event using a flow and log exception details to a custom object called Exception.

The exception object looks like below:


Create new Flow of type Platform-Event Triggered flow

And select auto-layout format.

Choose platform event as Batch Apex Error Platform Event


The flow will look like below at this stage:



Click on '+' symbol and add a 'Create Record' element.


Select Object as Exception:



And for field mapping select record  -BatchApexErrorEvent




The final mapping looks like below:


Save and activate this flow.

5. Exception Scenarios and BatchApexErrorEvent Behavior

Scenario 1 - Unhandled DML Exception

To produce a DML exception during contact update, I added a validation rule in contact, making Email field a required one.


Also I removed the Exception handling around contact update operation:


Now for some Accounts which has associated contacts, make Active value as No, and also make sure, it has contacts with Active as Yes and Email as empty.

Account:


Contact:


Now execute Batch:

Batch_ContactDeactivation reassign = new Batch_ContactDeactivation();
ID batchprocessid = Database.executeBatch(reassign,5);


Result:

Under Apex Jobs you can see that batch failed with DML exception due to validation error:



Now let us check the Exeption Object and we can see that a new Exception record got created with Exception details received from BatchApexErrorEvent.



Under Record ID - you can see that all contact Ids in the current scope got populated there.

This is utilizing the "JobScope" field of BatchApexErrorEvent.



Scenario 2 - Handled DML Exception

Now let us uncomment exception handling and execute once again.

Since we handled the exception, you can see under Apex jobs that it got completed withour throwing any error.


Since the exception got handled, no event got published under BatchApexErrorEvent also.

Same behavior will happen for a DML Exception, when we use Database.update instead of update.

Scenario 3 - Unhandled Query Exception

Let us see another exception scenario here.

In the query getting executed as part of batch, suppose the field name is added wrongly.

Example 
String query = 'select id from Contact WHERE account.Active__c=:inactive AND Active__c=:active';

In the above query instead of Active__c in contact, we added Active only by mistake:

String query = 'select id from Contact WHERE account.Active__c=:inactive AND Active=:active';

Now execute the batch.

Under Apex Jobs, we can see that batch execution failed:


Also we can see a new Exception record got created from BatchApexErrorEvent.


Here, record ID is empty since the query itself got failied and we don't have any records in the current scope.

So the above scenario is a good use case of unhandled exception which can be tracked using BatchApexErrorEvent.

In addition to the above, BatchApexErrorEvent will hold any kind of unhandled exception from Batch which include:

  • Batch Execution Limit related Exceptions
  • Unexpected Exception
  • System Exception
Another advantage of this approach is that, based on the Exception type if we would like to reexecute specific batches,
we can build a retry logic around these exception records.

References:

  • https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/sforce_api_objects_batchapexerrorevent.htm
  • https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_platformevents.htm

Comments

  1. Hi Meera , can you also show an example how u can we can achieve
    the below statement
    Another advantage of this approach is that, based on the Exception type if we would like to reexecute specific batches,
    we can build a retry logic around these exception records.

    ReplyDelete
    Replies
    1. My idea was around exceptions caused by Batch Apex limits/concurrent Access Issues etc. which might be a success if we rerun for those records later. So we can build a batch process around exception Object for the specific exception types and execute those specific failed batch processes, but only for those failed record Ids mentioned in Exception Record. Yes, might not be simple but I think it is feasible.

      Delete
  2. Hi Meera ,
    Thanks for the great explanation. I second Abhishek , i think many individuals will be benefitted by the re execute logic example.

    ReplyDelete
  3. Very informative article. Thank you

    ReplyDelete

Post a Comment

Popular posts from this blog

Subscribing to Salesforce Platform Events using External Java Client - CometD

Salesforce Security - Restriction Rules and Scoping Rules

How to develop reusable Invocable Apex methods for Flows