๐Ÿ’ƒ If at first you don't succeed ... ๐Ÿ•บ

... then implement a retry framework! ๐Ÿ’ฅ

Good morning, Salesforce Nerds! Do you know how healthy ๐Ÿ’ช your batch jobs are in your org(s)? ๐Ÿค” 

Are there super-critical jobs that run overnight you rush to check the results of first thing in the morning? ๐ŸŒ… 

You know the drill โ€ฆ ๐Ÿ‘ทโ€โ™‚๏ธ 

Setup โ–ถ๏ธ Environments โ–ถ๏ธ Jobs โ–ถ๏ธ Apex Jobs

Search feverously for overnight batch failures. ๐Ÿ˜ฐ 

Nooooooooooooo! ๐Ÿ‘Ž๏ธ There were a handful of failures! Time to put on that firefighting hat - there goes your morning plans. ๐Ÿ”ฅ๐Ÿง‘โ€๐Ÿš’๐Ÿš’

Ugh, this sucks. Donโ€™t get stuck in this cycle. ๐Ÿ›‘๐Ÿ”„ 

Truth is, thereโ€™s always going to be a reactionary element ๐Ÿ˜ตโ€๐Ÿ’ซ to building apps for our clients. ๐Ÿ’ฏ 

One of our jobs as Technical Architects and Developers is to mitigate this as much as possible. ๐Ÿง  

Today weโ€™re going to check out one way to do just that when it comes to handling exceptions in Batch Apex! ๐Ÿชฃ 

Agenda for today includes

  • If at first you donโ€™t succeed โ€ฆ

  • Daily Principle

  • All the Memes

If at first you donโ€™t succeed โ€ฆ

Today we will discuss -

๐Ÿ’ƒ The problem ๐Ÿ•บ

๐Ÿ’ƒ The solution ๐Ÿ•บ

๐Ÿ’ƒ Show me the code ๐Ÿ•บ

salesforce

Job Board | ๐Ÿ’ƒ Click Here ๐Ÿ•บ

Socials | ๐Ÿ’ƒ Linkedin | Instagram | Twitter!๐Ÿ•บ

๐Ÿ˜Ÿ The Problem

First, letโ€™s level ๐Ÿ“ set on what Batch Apex is โ€ฆ

โ€ฆ Batch Apex operates over small batches of records, covering your entire record set and breaking the processing down to manageable chunks โ€ฆ

Dude or Dudette that wrote this 

Basically - we can write code ๐Ÿ–ฅ๏ธ thatโ€™s only job is to run specialized business logic against huge ๐Ÿ“ data sets - think hundreds of thousands or even millions of records! ๐Ÿ’ฅ 

It does this by breaking down ๐Ÿ‘‡๏ธ the overall data set into smaller chunks (aka batches)! ๐Ÿ“ฆ๏ธ 

Hereโ€™s a quick visual: ๐Ÿ‘€ 

do the thing on 500k records in batches of 2000 records at a time

This is a fairly standard Batch processing workflow. ๐Ÿค˜ 

Got it? ๐Ÿ‘๏ธ If you need a little more - no worries - just hit these up:

Issues arise when batches start to fail. โŒ 

Remember, Batch Apex is a form of Asynchronous Apex. Ultimately, this means the batches run independently of one another. ๐Ÿ”€ 

But, they can also fail independently of one another too! ๐Ÿ˜“ 

Meaning, in our example above โ˜๏ธ, itโ€™s entirely possible for some of the batches to blow up. ๐Ÿ’ฅ 

Giving us something like this: ๐Ÿ‘‡๏ธ

Getting back to that mitigation thing โ€ฆ ๐Ÿค” 

These kinds of errors can be amongst the trickiest ๐Ÿช„ to solve. Many times requiring analysis to:

  1. Find the records that failed ๐Ÿ”๏ธ 

  2. Determine why they failed โœ๏ธ 

  3. โ€œFixโ€ the reason they failed ๐Ÿ› ๏ธ 

  4. Re-run the business logic against the โ€œfixedโ€ records โ–ถ๏ธ 

How can Architects and Developers solve for this? ๐Ÿงฉ 

Turns out the mothership has provided a solution! ๐Ÿ™ 

๐Ÿ˜ƒ The Solution

Itโ€™s actually pretty simple! ๐Ÿคฆ 

Itโ€™s just another interface! ๐Ÿ’ฅ 

Youโ€™ve herd us tout the importance of these in the past. Weโ€™ve even discussed other interfaces native to the platform you can leverage.

This is no different.

From a developerโ€™s perspective ๐Ÿ‘€ we just need to do 3 things:

  1. Add this code your batch class declaration

implements Database.RaisesPlatformEvents
  1. Add an after insert trigger to the BatchApexErrorEvent Platform Event

  2. Process the data from the Platform Event in the trigger

Thatโ€™s really it. ๐Ÿคท 

Of course, the obtuse part here is what does โ€œProcess the dataโ€ mean to you and your org? ๐Ÿค” 

This is up to your business and should fit your needs. Typically, I see some variation of one or all of the following: ๐Ÿ‘‡๏ธ 

โœ… Log the exception in a custom object. Thereโ€™s lots of useful data in the event record - reason for exception, the Idโ€™s of the records being operated on in the batch, the full stack trace, and more!

โœ… Decorate the records that failed processing with something that will notify a user it failed. Maybe add a link to the error log record or something to the Account if it failed?

โœ… Write another batch class that will automatically retry execution on all the failed batches that your persisted in your Custom Object!

Pretty slick stuff if you ask me! ๐Ÿ’…

โŒ No need to stress ๐Ÿ˜Ÿ if your batches are failing!

โŒ No need to dig through the Setup UI ๐Ÿ™„ to troubleshoot!

โŒ No need to analyze ๐Ÿงญ tons of data to figure out why batches are failing!

๐Ÿค“ Show me the code

Like we said up top ๐Ÿ‘†๏ธ , devs just need to do 3 things.

Letโ€™s see them in an example! ๐Ÿ™‚ Taken straight from the dev guide!

First, implement the interface in your batch class. ๐Ÿ’ป๏ธ This tells Salesforce to fire the Platform Event if an error happens during execution of this class:

public with sharing class YourSampleBatchJob implements Database.Batchable<SObject>, 
   Database.RaisesPlatformEvents{ 
   // class implementation 
}

Something to note โœ๏ธ here. There are no methods to implement from this interface - itโ€™s whatโ€™s known as a marker interface.

Moving on โ€ฆ ๐ŸŽฌ๏ธ 

Next, create an after insert trigger on the BatchApexErrorEvent Platform Event like so and fill it in with your custom logic:

trigger MarkDirtyIfFail on BatchApexErrorEvent (after insert) {
    Set<Id> asyncApexJobIds = new Set<Id>();
    for(BatchApexErrorEvent evt:Trigger.new){
        asyncApexJobIds.add(evt.AsyncApexJobId);
    }
    
    Map<Id,AsyncApexJob> jobs = new Map<Id,AsyncApexJob>(
        [SELECT id, ApexClass.Name FROM AsyncApexJob WHERE Id IN :asyncApexJobIds]
    );
    
    List<Account> records = new List<Account>();
    for(BatchApexErrorEvent evt:Trigger.new){
        //only handle events for the job(s) we care about
        if(jobs.get(evt.AsyncApexJobId).ApexClass.Name == 'AccountUpdaterJob'){
            for (String item : evt.JobScope.split(',')) {
                Account a = new Account(
                    Id = (Id)item,
                    ExceptionType__c = evt.ExceptionType,
                    Dirty__c = true
                );
                records.add(a);
            }
        }
    }
    update records;
}

Here weโ€™re just pulling data off the error event and adding some details on the original record that failed. This is where you could also log the error for later processing, too.

Weโ€™re skipping some best practices for brevity - in the real world you wouldnโ€™t want your code inside your trigger! ๐Ÿ’ฏ 

But, you can begin to see how useful this really is. ๐ŸŒฑ 

This event provides all the data we need to track and take action on our Batch Apex failures! โŒ 

So, now we can provide a full end-to-end batch solution that looks something like this: ๐Ÿฅณ๐Ÿ™Œ๐Ÿฅณ 

implement the interface, pull data off the error event in a trigger, do the needful

Do yourself and your clients a favor. ๐Ÿ™ Start planning to leverage this for any Batch Apex you implement!

Your client with love โค๏ธ it and youโ€™re solution will be that much stronger! ๐Ÿงฑ 

Daily Principle

"Never allow the same bug to bite you twice."

Steve Maguire

and now....Your Daily Memes

What did you think about today's newsletter?

Login or Subscribe to participate in polls.