Skip to main content

Quick Tips: Queueable Apex : Loose Coupling with Chainable Interface

Queueable Apex : Loose Coupling with Chainable Interface


Intro to Queueable Apex

Time and again, we come across use cases where we have to either perform an extensive processing, which can breach governor limits, or we want to perform some work asynchronously for improved user experience. Queueable apex provides us a means to create an asynchronous job (An asynchronous job means a piece of work, which is scheduled to run in background, for e.g. you may want to do custom rollup calculations on opportunity save).

Queueable apex is similar to future methods, wherein your future method is queued within a server queue. Server picks up the job and executes it, based on resource availability. However, there are some differences, quite notably:

  1. Queueable apex returns an ID for further enquiry
  2. Queueable apex methods can invoke external API (if Queueable apex class implements Database.AllowsCallouts interface)
  3. Queueable apex methods can enqueue other queueable apex jobs

Say, we have a simple Queueable job, which performs some work,as below:

public class QueueableApexDemo implements Queueable  {
    
    String msg;
    public QueueableApexDemo(String msg){
        this.msg = msg;
    }
    
    public void execute(QueueableContext context) {
        // Your processing logic here      
        system.debug(' This msg is = '  + msg);
    }
}


To execute this Queueable Apex class, you can simply use following apex code (anonymous apex):

QueueableApexDemo a = new QueueableApexDemo('JOB_A');
system.enqueueJob(a);

Chaining Queueable Apex jobs

Now, let's say we have to chain two jobs, say we have to run another job, after completion of initial job.

So, we create another Queuable class, such as:

public class ChainedJob implements Queueable  {
    
    String msg;
    public ChainedJob(String msg){
        this.msg = msg;
    }
    
    public void execute(QueueableContext context) {
        // Your processing logic here      
        system.debug(' ChainedJob msg is = '  + msg);
    }
}


Now, in order to chain these, we'll need to modify initial class QueueableApexDemo as follows:

public class QueueableApexDemo implements Queueable  {
    
    String msg;
    public QueueableApexDemo(String msg){
        this.msg = msg;
    }
    
    public void execute(QueueableContext context) {
        // Your processing logic here      
        system.debug(' This msg is = '  + msg);
        
        ChainedJob job2 = new ChainedJob('CHAINED_JOB_1');
        System.enqueueJob(nextJob);
    }
}


Note: A key limitation to consider here is that Apex allows only one job to be enqueued via a Queueable job. 

Tight coupling with Queueable Apex

Now, in real world, you would not want your first class (QueueableApexDemo) is tightly coupled with second class (ChainedJob). Also, in reality, as you create more complex code, there may be need to dynamically decide jobs to be chained together at runtime.

QueueableChainJob

I created a simple wrapper class to address this complexity around chaining queueable apex jobs at runtime, while keeping individual queueable apex classes loosely coupled. Another key design factor I kept in mind was to not add a complete wrapper on the Queueable apex class, rather extend its capabilities.


public abstract class QueueableChainJob {
    
    private Queueable nextJob;
    
    public void setNextJob(Queueable nextJob){
        //Apex does not allow chaining queueable jobs within Test Context;
        if(Test.isRunningTest() == false){
            this.nextJob = null;
        }
    }
    
    public Queueable getNextJob(){
        return this.nextJob;
    }
    
    public ID enqueueNextJob(){
        ID jobID = null
        if(nextJob != null){
            jobID = System.enqueueJob(nextJob);
        }
        return jobID; 
    }
}


Implement Loose Coupling with Chainable Interface

Now, you can modify your Queuable Apex classes such as:

First class: QueueableApexDemo

public class QueueableApexDemo extends QueueableChainJob implements Queueable  {
    
    String msg;
    public QueueableApexDemo(String msg){
        this.msg = msg;
    }
    
    public void execute(QueueableContext context) {
        // Your processing logic here      
        system.debug(' This msg is = '  + msg);
        
        // Chain this job to next job by submitting the next job
        ID jobID = this.enqueueNextJob();
    }
}

Second Class: ChainJob

public class ChainedJob extends QueueableChainJob implements Queueable {
    
    String msg;
    public ChainedJob(String msg){
        this.msg = msg;
    }
    
    public void execute(QueueableContext context) {
        // Your processing logic here      
        system.debug(' ChainedJob msg is = '  + msg);
        
        // Chain this job to next job by submitting the next job
        ID jobID = this.enqueueNextJob(); 
    }
}

Please note, there are minor differences:
  1. both classes now extend QueueableChainJob abstract class
  2. both classes have execute method calling enqueueNextJob();
Rest of the code remains as-is.

Now, you can run your code as follows:

QueueableApexDemo a = new QueueableApexDemo('JOB_A');
ChainedJob b = new ChainedJob('JOB_B');

a.setNextJob(b);

system.enqueueJob(a);

Similarly, you can chain multiple jobs as needed and add required conditions to add jobs. Sample code below:

QueueableApexDemo nextChainJob;
QueueableApexDemo a = new QueueableApexDemo('JOB_A');
nextChainJob = a;

if(UserInfo.getUserType() == 'Standard'){
    ChainedJob b = new ChainedJob('JOB_B');

    nextChainJob.setNextJob(b);

    nextChainJob = b;
}

In above code, the second job is chained, only if current user's UserType is Standard, else second job is not chained.

The above code is meant for guidance is not tested for all scenarios.

Comments

Popular posts from this blog

Lightning: Generate PDF from Lightning components with in-memory data

I'm sure as everyone is diving into lightning components development, they are getting acquainted with the nuances of the Lightning components framework. As well as, its current limitations. Being a new framework, this is bound to happen. Although we have our users still using salesforce classic, we have started using lightning components framework our primary development platform and Visualforce is considered primarily for rendering lightning components within Classic Service console. Recently, while re-architecting a critical module, we encountered a problem wherein we needed to generate PDF from lightning components. Now, being Javascript intensive framework, it has limited room for such features (may be included in future roadmap). As of now, there is no native feature within the lightning framework to do so (at least I didn't find anything). Common Scenario - Create Visualforce page to retrieve data and generate PDF For scenarios where the data exist within Sa

Quick Tips: Salesforce default Images

Well, I'm sure a lot of you still rely on using out of the box salesforce images for displaying quick icons within formula fields or even using them within your Visualforce pages. Lately, I realized that a lot of earlier resources are no longer accessible, so I tried to quickly extract all images from Salesforce CSS files and provide a quick reference here. Please note, I've referenced all images from SF servers directly, so if anything changes, the image should stop rendering here. As these images are completely controlled by Salesforce, and in case they change anything, it might lead to image not being accessible. Image path Image /img/samples/flag_green.gif /img/samples/flag_green.gif /img/samples/flag_red.gif /img/samples/color_red.gif /img/samples/color_yellow.gif /img/samples/color_green.gif /img/samples/light_green.gif /img/samples/light_yellow.gif /img/samples/light_red.gif /img/samples/stars_100.gif /img/samples/stars_200.gif /img/samples/stars_300.

Lightning: Generate PDF within Lightning Experience with Salesforce Data

Some time back I posted a solution to generate PDF from Lightning components using in-memory data. Post url: http://www.vermanshul.com/2017/07/lightning-generate-pdf-from-lightning.html It was developed for a specific scenario, wherein we need to generate PDF where: User interface is Salesforce classic Initiated via Lightning Component Data doesn't exist within Salesforce and is completely in-memory As complex and tricky this situation was, we did end up finding a stable and equally tricky solution. However, I realize that there are still lack of solutions (or maybe my search skills are downgrading) to generate and automatically download PDF document from Lightning Experience, without using any lightning components, wherein data exists within Salesforce. You can use the earlier solution in that case, but it will be an overkill. There are various solutions available to generate PDF from javascript. But, I still think the plain old method of converting HTML to PDF (via