Let me throw around a scenario and let's understand how do we handle such scenario before Spring 21.

From Queueable Apex, when we try to make a callout to an external platform, because of a blip in the network, if the callout fails, how do we make sure that the callout it made again and re-enqueued?

We need to get the JobId after the callout is made, then check the status of the Job, if it failed then we need to re-enqueue it manually.

Salesforce team launched something called Finalizers which will make the whole process of re-enqueueing the failed queueable job a breeze.

Let's look at an example!

public class SampleAsyncJob implements Queueable {
    
    public void execute(QueueableContext ctx) {
        
        UdpateAWSFinalizer f = new UdpateAWSFinalizer();
        System.attachFinalizer(f);
        
        //Business logic
    }
    
}
SampleAsyncJob.cls
public class UdpateAWSFinalizer implements Finalizer {

   
    public void execute(FinalizerContext ctx) {
		
        //getRequestId()
        //getAsyncApexJobId()
        //getResult()
        //getException()
        
        String reqId = ctx.getRequestId();
        String jobId = Id.valueOf(ctx.getAsyncApexJobId());
        
        if (ctx.getResult() == ParentJobResult.SUCCESS) {
            System.debug('Parent Queueable (job id: ' + jobId + '): completed successfully!');

        } else { // Queueable failed
            //provide a counter & it should not exceed 5 times
            //re-enqueue the job
        }
    }

}
UdpateAWSFinalizer.cls

This is what we are trying to do in the example.

SampleAsyncJob is a Queueable job which has some business logic associated with it(something like making a callout or etc.)

I created another class that implements Finalizer.

Finalizer is an interface and it overrides execute().

execute() accepts a single parameter of type FinalizerContext

FinalizerContext is a class that provides us access to 4 methods.

  • getRequestId()
  • getAsyncApexJobId()
  • getResult()
  • getException()

In UdpateAWSFinalizer we are trying to check if the status of the parent job is successful or failure.

if (ctx.getResult() == ParentJobResult.SUCCESS) {
        
} else { // Queueable failed
    //provide a counter
}    

If the status of the parent job is a failure then we have to re-enqueue the business logic again.

There is a limit here as well, we cannot re-enqueue the job beyond 5 times.