How to choose the best cloud platform for AI
Explore a strategy that shows you how to choose a cloud platform for your AI goals. Use Avenga’s Cloud Companion to speed up your decision-making.
How to run a background job in Salesforce every single minute.
Automated flow in Salesforce is pretty straightforward. In general, the system reacts to data changes or UI events. For example, user update field value – system run validation rules, triggers, flows, etc.
But sometimes we need to activate the flow by time. For example, execute every 10 minutes:
Such an approach is very handy when we do the same actions over and over. Look at the list of possible business requirements below:
The goal of this article is to describe possible solutions for running a background job every N minutes.
Salesforce suggests the use of a special Apex interface – Schedulable to run a background job at regular intervals. An administrator can schedule the job in two ways:
Starting a scheduled job from the Developer Console has some benefits:
Look at the code snippet below:
System.schedule(‘Delete old logs’, ‘0 15 3 * * ?’, new LogBatch());
This command line creates an instance of LogBatch Apex class and schedules it with a job name ‘Delete old logs’ at 3:15 AM every day.
It is easy to understand the CRON expression. It has strict syntax:
It is possible to run a job every N hours or days by adding /N to the relevant item. For example, 0 0 */3 * ? means that the job will be running every 3 hours.
If you try to do the same with minutes (0 */3 * * ?) you will be disappointed. The system will deny such a schedule with an error message “Seconds and minutes must be specified as integers.” A CRON expression to run a job every N minutes is not supported by Salesforce yet.
What do you do if you need to run a scheduled job every 5 minutes? Of course, you can always schedule the job up to 12 times, with intervals of 5 minutes:
0 0 * * * ?
0 5 * * * ?
0 10 * * * ?
…
0 55 * * * ?
Looks really weird, doesn’t it?
Note, it consumes 12% of the limit for Apex classes scheduled concurrently. Only 100 active scheduled jobs are available.
Scheduling jobs in small intervals has one more side effect and it depends upon the time of the job execution. Very often you need to schedule a batch job. Batch jobs have their own limitation of 5 classes scheduled concurrently. They can be placed in the Apex flex queue with Holding status and wait until resources become available. So, it is possible to have a situation where a scheduled job runs another instance of batch, but a previous batch job is running. It could be even worse – it is still waiting for execution:
Every time you need to schedule a long running job – be careful. Try to decrease the execution time of the job or increase intervals between scheduled jobs.
A scheduled job is a special type of asynchronous Apex. You can specify the execution time but the actual execution may be delayed based on service availability. In other words Salesforce does not guarantee the exact time when the scheduled job will be executed.
Maybe the best option to run a job at a specified time is to use an external service like Heroku. External applications can initiate action on the Salesforce side through API:
Of course this approach adds extra complexity to your solution. We have to develop and support the external application. Also, such actions take place in the foreground mode, which has stricter limits. If exact running time really matters – do it this way.
We still are able to run a job every minute by pure Apex. The idea is based on having 2 scheduled jobs: main and worker. The system administrator schedules the main job in a minimum allowed interval (1 per hour). The main job dynamically creates a one time job (worker) and those jobs run one by one in a row:
Let me explain the solution in more detail:
The whole code of a scheduled job can be found below:
public class EveryMinuteJob implements Schedulable {
private Integer delayInMinutes;
private CronTrigger mainJob;
// Constructor of Main job
public EveryMinuteJob(Integer delayInMinutes) {
this.delayInMinutes = delayInMinutes;
}
// Constructor of worker job
private EveryMinuteJob(Integer delayInMinutes, CronTrigger parentJob) {
this(delayInMinutes);
mainJob = parentJob;
}
public void execute(SchedulableContext sc) {
Id jobId = sc.getTriggerId();
if (mainJob == null || mainJob.Id == jobId) {
mainJob = [
SELECT CronJobDetail.Name, NextFireTime
FROM CronTrigger WHERE Id = :jobId
];
} else {
System.abortJob(jobId);
}
Datetime nextWorkerRun = Datetime.now().addMinutes(delayInMinutes);
if (nextWorkerRun.addMinutes(delayInMinutes) < mainJob.NextFireTime) {
String cronExp = nextWorkerRun.second() + ' '
+ nextWorkerRun.minute() + ' '
+ nextWorkerRun.hour() + ' '
+ nextWorkerRun.day() + ' '
+ nextWorkerRun.month()
+ ' ? '
+ nextWorkerRun.year();
System.schedule(
mainJob.CronJobDetail.Name + ' (worker)',
cronExp,
new EveryMinuteJob(delayInMinutes, mainJob)
);
}
try {
// run action here
} catch(Exception ex) {
// log errors here
}
}
}
Using background scheduled jobs is a very powerful technique in Salesforce. With async Apex you can achieve results that cannot be done in other ways. However it could be pretty tricky to schedule a job every N minutes. Think of using an external service as your plan B.
* US and Canada, exceptions apply
Ready to innovate your business?
We are! Let’s kick-off our journey to success!