Scheduled Jobs in Salesforce

Scheduled Jobs
in Salesforce

Scheduled Jobs

How to run a background job in Salesforce every single minute.

Business case

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.

Business case background job in Salesforce

But sometimes we need to activate the flow by time. For example, execute every 10 minutes:

Activate Salesforce flow

Such an approach is very handy when we do the same actions over and over. Look at the list of possible business requirements below:

  1. Read data from external API and update a custom object every 5 minutes.
  2. Data depends on time or formula field; we have to track changes.
  3. Users update data intensively. For performance reasons we have to postpone data aggregation to every couple of minutes.
  4. A job should be run on behalf of each different user. This user may have special permissions or licenses.
  5. There is no way to subscribe to changes in an object via automation tools like Workflow Rule, Process Builder or Apex Trigger.

The goal of this article is to describe possible solutions for running a background job every N minutes.

Problems with best practices

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:

  • From the Setup menu (Custom Code /Apex Classes / Schedule Apex)
  • By using the Developer Console

Starting a scheduled job from the Developer Console has some benefits:

  • CRON expression (administrators are familiar with its syntax)
  • End time field is not required

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:

CRON expression

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.

Possible solutions

Schedule multiple jobs

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:

Scheduling jobs in Salesforce

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.

Use external services

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:

Heroku CRON job Salesforce 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.

Pure Apex solution with dynamic scheduling

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:

 Apex solution with dynamic scheduling

Let me explain the solution in more detail:

  • Our scheduled Apex class needs at least one parameter (delayInMinutes) to specify the interval in minutes between job executions.
  • It has two constructors: public for the main job and private for the worker job.
  • The approach consumes only 2 scheduled jobs (main and worker). We have to delete the CronTrigger record of a worker job from the code.
  • The CronTrigger record of a main job contains the time of the next execution – NextFireTime field. We will run worker jobs until their start time less.
  • To run a worker job we build a one time CRON expression and specify all items except the day of the week.
  • For a worker job we use the same job name with an additional “(worker)” suffix.
  • The code of action (what the background job is actually going to do) should be surrounded by a try … catch block. Any exceptions prevent the scheduling of a worker job.

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
        }
    }
    
}

Summary

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.

Other articles

or

Book a meeting

Call (Toll-Free*) +1 (800) 917-0207

Zoom 30 min

* US and Canada, exceptions apply

Ready to innovate your business?

We are! Let’s kick-off our journey to success!