Introduction
Salesforce provides a way to process records asynchronously, such as by using Future Methods, Queueable Apex, Batch Apex, and Scheduled Jobs to handle long-running or resource-intensive operations.
However, improper usage of these asynchronous mechanisms can result in an AsyncException.
In this blog, we’ll dive into:
- What is AsyncException?
- Common scenarios that cause it.
- Examples of errors.
- Best practices to avoid AsyncException.
If you’re unfamiliar with Asynchronous Apex in Salesforce, such as Future Method, Batch Apex or Queueable Apex, I recommend checking out the post linked below before proceeding with this one.
What is AsyncException?
AsyncException occurs when there’s an issue in executing asynchronous operations due to platform limitations or misuse of asynchronous features.
Salesforce imposes limits on the number of asynchronous jobs to ensure stability and fairness in its multi-tenant architecture.
Common Scenarios Where AsyncException Occurs
1. Too Many Asynchronous Jobs Queued
Salesforce allows a maximum of 50 asynchronous jobs (including Queueable, Future, or Scheduled Jobs) per transaction.
Exceeding this limit throws an AsyncException.
2. Duplicate Scheduled Job Names
When scheduling a job using the System.schedule method, the job name must be unique. If the same name is used for multiple scheduled jobs, an AsyncException is thrown.
3. Too Many Active Batch Jobs
Salesforce limits the number of active batch jobs to 5 at any given time. Active jobs include jobs with the status Processing or Queued.
Attempting to execute more batch jobs results in an AsyncException.
4. Too Many Future Method Calls
The maximum number of future method calls allowed per transaction is 50. If this limit is exceeded, Salesforce throws an AsyncException.
Here’s a detailed explanation of each point mentioned above, along with examples:
Examples of AsyncException
Example 1: Too Many Asynchronous Jobs
public class AsyncJobExample {
public static void enqueueJobs() {
try {
for (Integer i = 0; i < 100; i++) {
System.enqueueJob(new MyQueueableJob()); // Exceeds 50-job limit
}
} catch (AsyncException e) {
System.debug ('AsyncException: ' + e.getMessage());
}
}
}
public class MyQueueableJob implements Queueable {
public void execute(QueueableContext context) {
System.debug('Processing job...');
}
}
- Error: Too many queueable jobs added to the queue: 51
- Fix: Ensure no more than 50 jobs are queued in a single transaction. Process jobs in smaller batches.
Example 2: Duplicate Scheduled Job Names
public class ScheduleJobExample {
public static void scheduleJobs() {
try {
String jobName = 'DailyJob';
System.schedule(jobName, '0 0 12 * * ?', new MyScheduledBatch());
System.schedule(jobName, '0 0 18 * * ?', new MyScheduledBatch()); // Duplicate name
} catch (AsyncException e) {
System.debug ('AsyncException: ' + e.getMessage());
}
}
}
public class MyScheduledBatch implements Schedulable {
public void execute(SchedulableContext context) {
System.debug('Executing scheduled job...');
}
}
- Error: AsyncException: Job name already exists
- Fix: Use unique job names.
String jobName = 'DailyJob_' + System.currentTimeMillis ();
Example 3: Too Many Active Batch Jobs
public class BatchJobExample {
public static void executeBatchJobs() {
try {
for (Integer i = 0; i < 10; i++) {
Database.executeBatch (new MyBatchJob(), 200); // Exceeds 5-job limit
}
} catch (AsyncException e) {
System.debug ('AsyncException: ' + e.getMessage());
}
}
}
public class MyBatchJob implements Database.Batchable {
public Database.QueryLocator start (Database.BatchableContext context) {
return Database.getQueryLocator ('SELECT Id FROM Account');
}
public void execute (Database.BatchableContext context, List scope) {
System.debug('Processing batch...');
}
public void finish (Database.BatchableContext context) {
System.debug('Batch job finished.');
}
}
- Error: Too many batch jobs in progress
- Fix: Ensure the number of active batch jobs (including queued ones) is less than 5.
Example 4: Too Many Future Method Calls
public class FutureMethodExample {
public static void invokeFutureMethods() {
try {
for (Integer i = 0; i < 100; i++) {
MyFutureClass. callFutureMethod(); // Exceeds 50 future calls
}
} catch (AsyncException e) {
System.debug ('AsyncException: ' + e.getMessage());
}
}
}
public class MyFutureClass {
@future
public static void callFutureMethod() {
System.debug('Processing future method...');
}
}
- Error: Too many future calls: 51
- Fix: Use bulkified methods or asynchronous strategies like Batch Apex to reduce future calls.
Best Practices to Avoid AsyncException
1. Monitor Asynchronous Job Limits
Use Limits.getLimitFutureCalls() or query the AsyncApexJob object to check the current usage of asynchronous jobs.
Integer activeJobs = [SELECT COUNT() FROM AsyncApexJob WHERE Status IN ('Processing', 'Queued')];
System.debug('Active jobs: ' + activeJobs);
2. Use Unique Job Names for Scheduled Jobs
Generate unique names for scheduled jobs using a timestamp or unique identifier.
String jobName = 'Job_' + System.currentTimeMillis ();
System.schedule(jobName, '0 0 12 * * ?', new MyScheduledBatch());
3. Chain Jobs Using Queueable Apex
Instead of scheduling multiple jobs simultaneously, chain them using System.enqueueJob() in the execute method of a Queueable class.
Example:
public class MyQueueableJob implements Queueable {
public void execute(QueueableContext context) {
System.debug('Processing job...');
if (someCondition) {
System.enqueueJob(new MyQueueableJob()); // Chain the next job
}
}
}
4. Use Batch Apex for Large Data Sets
For operations involving large datasets, use Batch Apex to break the transaction into smaller chunks and avoid asynchronous limits.
Apart from AsyncException, Salesforce developers may encounter various other exceptions while designing and implementing various Salesforce business requirements.
Explore the post below to learn about these different types of exceptions, complete with examples.
Conclusion
The AsyncException is a safeguard against improper usage of Salesforce’s asynchronous capabilities.
By understanding its causes and adhering to best practices like monitoring limits, using unique job names, chaining jobs, and employing Batch Apex, you can avoid these errors and design scalable, efficient Apex solutions.