Learn MOAR in Spring ’20 Introducing Transaction Finalizers

Discover Spring ’20 Release features! We are sharing release highlights for Developers (and Admins), curated and published by Salesforce product experts, as part of Learn MOAR. Complete the trailmix by March 31, 2020 to receive a special community badge and unlock a $10 contribution to FIRST®.

The Spring ’20 release brings a new feature to Apex: Transaction Finalizers. They’re my favorite feature of Apex to land since the Switch statement, and I’m sure you’ll be fan by the time this post is over! We’ll look at what Finalizers are, how they work, and where you’d want to use them.

So what’s a Finalizer?

Simply put, Finalizers are developer defined Apex classes that run when a Queueable job finishes. Let’s unpack that a bit. Finalizers are developer defined, which means you control their construction, and logic. They’re classes that implement the finalizer system interface. Additionally, a single Finalizer can be optionally attached to any Queueable class using a new System method.

As you can see, the idea is very simple. As you refactor or create Queueables, you can attach a Finalizer. When the system is done processing your Queueable, it will run the attached Finalizer. The only question then, becomes what can we do with this feature? Print ‘Hello World’ of course!

An example Finalizer class

In the finest tradition of software development, let’s build a ‘Hello World’ Finalizer. Let’s use this for our Queueable:

public class ExampleQueuable implements Queueable {
  public void execute(QueueableContext context) {
    System.debug('Look at me, doing something awesome, asynchronously');
  }
}

To implement a Finalizer for this Queueable, we need to do two things: 1. Create a class that implements the Finalizer interface; and 2. Assign the Finalizer class to the Queueable. Here’s how to implement the Finalizer class:

// Note that this clsass implements the Finalizer interface
public class HelloWorldFinalizer implements Finalizer {
  // This is the one method we have to write to implement Finalizer. 
  // You might call it... the Final Method.
  public void execute(FinalizerContext context){
    System.debug('Hello World');
  }
}

Refactoring our Queueable to utilize this Finalizer is equally straightforward. First, we construct an instance of the Finalizer. Then we call the new System.attachFinalizer() method. Our finished Queueable with Finalizer would then look like this:

public class ExampleQueuable implements Queueable {
  public void execute(QueueableContext context) {
    // Create an instance of our Finalizer class
    HelloWorldFinalizer mostEpicFinalizer = new HelloWorldFinalizer();
    // Call System.attachFinalizer() with our finalizer instance
    System.attachFinalizer(mostEpicFinalizer);
    System.debug('Look at me, doing something awesome, asynchronously');
  }
}

Now, whenever ExampleQueuable runs it will print ‘Look at me, doing something awesome, asynchronously’. When that Queueable completes, it will print ‘Hello World’. With the Hello World example out of the way let’s look at some of the more interesting things you can do with Finalizers.

Practical uses of Finalizers

Our example above is artificially basic. In a real org, there are plenty of reasons you might want to use a Finalizer. I want to specifically draw your attention to three of them. First, Finalizers are reusable. You can attach the same Finalizer to multiple Queueable jobs and utilize a consistent, common codebase for post-async actions. Secondly, the Finalizer class’ execute method is passed a FinalizerContext object by the system when it’s executed. This context object has methods to access the Queueable’s jobId as well as its completion status. Additionally, if there were exceptions executing the Queueable, you can access them via the Context object. Let’s look at a more useful Finalizer implementation:

public class HelloWorldFinalizer implements Finalizer {

  public void execute(FinalizerContext context){
    Id parentQueueableJobId = context.getAsyncApexJobId();
    switch on context.getAsyncApexJobResult() {
      when SUCCESS {
        System.debug('Hello World. Job ID: ' + parentQueueableJobId + ' Succeeded');
      }
      when UNHANDLED_EXCEPTION {
        System.Debug('OH NOES! (Job ID: ' + parentQueueableJobId +
          '): FAILED! with error: ' + 
          context.getAsyncApexJobException().getMessage());
      }
    } 
  }
}

This Finalizer uses the getAsyncApexJobResult() method for flow control. When the enum returned by that method is SUCCESS, it takes one series of actions. When it returns UNHANDLED_EXCEPTION it takes a different set of actions. This leads me to the final, most important feature of Finalizers. Utilizing job flow control, enables you to intelligently make decisions about potentially re-enqueueing the job. If, for instance, your Queueable failed and the unhandled exception is your custom ApiTimeoutError, you could re-enqueue the job. On the other hand, if you get a DML error, perhaps you want to email your development team. On success you could fire a third party API call to sync new data, or enqueue the next stage in your asynchronous process workflow. Likewise, developers can also write Finalizers to fire platform events, or send emails.

Every Feature has its Governor Limits

It’s true. All features have their limits, and Transaction Finalizers are no exception. Thankfully, there are only two things to keep in mind. First, you can only attach a single Finalizer to a Queueable. Two, whatever work you do inside the Finalizer is still subject to the standard governor limits on CPU time, DML limits etc. That said, it’s important to note that the Queueable and the Finalizer run in separate Apex Transactions! For instance, you can do DML in your Queueable, and make a REST callout in your Finalizer.

So that’s Finalizers!

Finalizers are the amazing new pilot feature that enables you to define what happens when the Queueable finishes. They’re my favorite new feature of Apex since the humble Switch statement. With Finalizers developers get flow control and automation on top of Queueables.

Here in the next few days I’ll post about how I implement the promise pattern in Apex using Finalizers. I’m curious to see what YOU will build! Will you harness Queueables, Finalizers, and Events to build a robust error logging system? A multiplexer for Finalizers? (Chris Peterson has a lead on that over here). Of course if you’re new to Queueables I want to draw your attention to this Trailhead module: Asynchronous Apex that covers not just Queueables but Future, Batch and Scheduled Apex as well. Hit us up on Twitter @SalesforceDevs with details on what you’ve built!