Generic Approach to Salesforce Queueable : Aykut Parlak
by: Aykut Parlak
blow post content copied from Apex Hours
click here to view original post
Salesforce has a good capability of asynchronous execution. For instance future, batch, schedulable and queueable methods in apex. Although it has good async methods, the challenge is a missing framework. Basically, it has nice tools to execute the async job but it requires an orchestration mechanism. As a developer, most of the time you need to develop your own set of classes/methods to handle this.
In this article, I would like to mention a generic queueable method. It is the upgraded version of the future method. It resolves significant restrictions of it (you can read an article here). Mainly you can chain jobs within queueable methods. But it has still some restrictions which let our lives not easier (list of the restrictions). Especially this:
When chaining jobs, you can add only one job from an executing job with System.enqueueJob, which means that only one child job can exist for each parent queueable job. Starting multiple child jobs from the same queueable job isn’t supported
This means that if we want to make multiple queueable method call in one transaction then we need to create a logic to handle the chaining in the queueable class itself. This brings complexity to the usage of this async method.
Therefore I have created a generic method that can handle thousands of jobs in one transition. Basically, I have created an AsyncJobService.csl and you can add the jobs in queueable context, then afterwords run them in a chained way.
Adding a new job:
AsyncJobService.addJob(jobName, className, methodName, parameters);
Running them:
AsyncJobService.runJobInQueues();
Technical Overview
This implementation simply consists of three parts.
- A generic job object, AsyncJob.cls
- A callable class for class/method configuration, GenericCallable.cls
- A service class for orchestration of them, AsyncJobService.cls
The service class is preparing and adding the jobs to a list and at the end, it calls the generic callable class to process them. The generic callable class goes through the list of jobs and runs them one by one in the queueable context.
The code can be found under this link. Although there is room to improve it, it works pretty well. If you want to use a method with this generic queueable class, first you need to create a static method and then configure it in GenericCallable.cls.
Code View
First, we are defining the object type
/***********************************************************************************************************************
Apex Class Name : AsyncJob
Version : 1.0
Created Date : 30.03.2021
Function : AsyncJob object definition
Modification Log :
* Developer Date Description
* ----------------------------------------------------------------------------------------------------------------------
* Aykut Parlak 30/03/2021 Initial version
***********************************************************************************************************************/
public without sharing class AsyncJob {
public String jobName;
public String jobClass;
public String jobMethod;
public String jobStatus;
public String jobError;
public List<Id> recordIds;
public AsyncJob(String jobName, String jobClass, String jobMethod, List<Id> recordIds) {
this.jobName = jobName;
this.jobClass = jobClass;
this.jobMethod = jobMethod;
this.recordIds = recordIds;
}
}
The data structure is designed for working with record ids. Then we are having a generic queueable method:
/***********************************************************************************************************************
Apex Class Name : GenericQueueable
Version : 1.0
Created Date : 30.03.2021
Function : GenericQueueable class
Modification Log :
* Developer Date Description
* ----------------------------------------------------------------------------------------------------------------------
* Aykut Parlak 30/03/2021 Initial version
***********************************************************************************************************************/
public class GenericQueueable implements Queueable, Database.AllowsCallouts {
private List<AsyncJob> jobs;
private Integer currentJobNumber;
public GenericQueueable(List<AsyncJob> jobs, Integer currentJobNumber) {
this.jobs = jobs;
this.currentJobNumber = currentJobNumber;
}
public GenericQueueable(List<AsyncJob> jobs) {
this.jobs = jobs;
this.currentJobNumber = 0;
}
public void execute(QueueableContext context) {
GenericCallable gc = new GenericCallable();
gc.call('apexQueue', new Map<String, Object>{ 'jobs' => this.jobs, 'currentJobNumber' => this.currentJobNumber });
}
}
We need a service class to orchestrate the job processes:
/***********************************************************************************************************************
Apex Class Name : AsyncJobService
Version : 1.0
Created Date : 30.03.2021
Function : AsyncJob Service class
Modification Log :
* Developer Date Description
* ----------------------------------------------------------------------------------------------------------------------
* Aykut Parlak 30/03/2021 Initial version
***********************************************************************************************************************/
public with sharing class AsyncJobService {
public static AsyncJob[] jobs;
public static void addJob(AsyncJob job) {
if (jobs != null)
jobs.add(job);
else {
jobs = new List<AsyncJob>();
jobs.add(job);
}
}
public static void addJob(String jobName, String jobClass, String jobMethod, List<Id> recordIds) {
AsyncJob job = new AsyncJob(jobName, jobClass, jobMethod, recordIds);
addJob(job);
}
public static void runJobInQueues() {
if (jobs == null || jobs.isEmpty())
return;
if (Test.isRunningTest())
jobs = new List<AsyncJob>{ jobs[0] };
GenericQueueable gq = new GenericQueueable(jobs);
System.enqueueJob(gq);
jobs.clear();
}
}
The last class is the class where we can register our methods:
/***********************************************************************************************************************
Apex Class Name : GenericCallable
Version : 1.0
Created Date : 30.03.2021
Function : GenericCallable class
Modification Log :
* Developer Date Description
* ----------------------------------------------------------------------------------------------------------------------
* Aykut Parlak 30/03/2021 Initial version
***********************************************************************************************************************/
public without sharing class GenericCallable implements Callable {
public Object call(String action, Map<String, Object> args) {
switch on action {
when 'apexQueue' {
Integer currentJobNumber = (Integer) args.get('currentJobNumber');
Integer nextJobNumber = currentJobNumber + 1;
List<AsyncJob> jobs = (List<AsyncJob>) args.get('jobs');
AsyncJob job = jobs[currentJobNumber];
// here you can implement the new class/method
try {
if (job.jobClass == 'System' && job.jobMethod == 'debug') {
System.debug(job);
} else {
throw new GenericCallableException('Method not implemented');
}
job.jobStatus = 'OK';
} catch (Exception e) {
job.jobStatus = 'ERROR';
job.jobError = e.getMessage();
}
if (nextJobNumber < jobs.size()) {
GenericQueueable gq = new GenericQueueable(jobs, nextJobNumber);
System.enqueueJob(gq);
}
}
when else {
throw new GenericCallableException('Method not implemented');
}
}
return null;
}
public class GenericCallableException extends Exception {}
}
When we have a class/method for the job logic we can implement it here.
Summary
Apex Queueable method is enhanced with a generic async approach and we can chain thousand of jobs with easy add and run methods. It is good but not perfect so it requires improvement and this will be the next version of this implementation.
The post Generic Approach to Salesforce Queueable appeared first on Apex Hours.
November 04, 2021 at 07:30PM
Click here for more details...
=============================
The original post is available in Apex Hours by Aykut Parlak
this post has been published as it is through automation. Automation script brings all the top bloggers post under a single umbrella.
The purpose of this blog, Follow the top Salesforce bloggers and collect all blogs in a single place through automation.
============================
Post a Comment