Salesforce has four asynchronous apex ( Queueable Apex, Scheduled Apex, Batch Apex, Future Methods) to run code asynchronously to handle different need of business application. These apex is used for syncing data in two systems, handling longer jobs which will hit Salesforce Governor limits. These asynchronous apex can be used in different use case. This post is to explain where to avoid batch apex and use Queueable Class instead. Although there are lot of use cases where batch job is sufficient to handle processing.
This post will explain how to handle callout issue You have uncommitted work pending. Please commit or rollback before calling out
Use Case To Solve:
We have around 30K+ account records of health care providers, we need to validate address of all records using address API (any API like SmartyStreet, Google Places, ZipCode ). After API call we need to update record with outcome of API call like address validation is successful or failed in ValidationStatus field. ValidationStatus is custom field in account object. All health care data are stored in account object.
To solve this use case we can use batch apex which will handle 200 records (100 in case of callout in batch) in a single batch and batch job will be created for all records. As it will run in batch, we have to put some condition in batch SOQL so that it will not process same record again and again. So we can use query like
Select id, ValidationStatus__c from Account where ValidationStatus__c=false
This will only process record which is pending but there is another problem with this approach. As validation is not successful due to some reason that record will be pending and same records will be fetched in each job for process. This issue we can handle using adding another filter last modified date in SOQL
Select id, ValidationStatus__c from Account where ValidationStatus__c=false and LastModifiedDate<>TODAY
Using this batch it will solve issue of not processing same record again and again. We will face another problem after that is , if we want to re-process pending record or error records after correction, it will not re-process those record on same day.
So what will be efficient solution for our use case. Can we use another asynchronous apex as we have lot of records to process? Yes, we have to use another asynchronous apex Queueable to handle this. We can query all pending records in one parent queueable class and then process records in another queueable class which will support callout. We will run chain of Queueable child class to process 100 records at once. Once 100 records are processed, start another queueable child job. This way we can call external system and make list of records to update before we start another job. This way we can avoid uncommitted work pending issue also.
Uncommitted work pending issue throws when we are calling external system and then updating response in object, when system start processing next record, previous records is still pending for commit so we get that error. This issue will be address by below approach
- We will call external callout for reach record one by one
- We will not update records immediately, instead we will add all records in collection
- Once 99 records are processed, we will start new queueable job for processing another set of 99 records
- Before we start new batch, we will update all records for status update.
Above approach will handle issue uncommitted work pending for commit issue.
Let us see code for this solution
Above queueable apex code is getting all records which need to process and passing all records to another child queueable class. As we are passing all records to child queueable class we don’t require to run this class multiple time. So if any error occurs for any records and we want to re-process those records, we can run this class once again after record correction.
Let us see code for child queueable class which will process each record and also handle uncommitted code pending issue.
AddressVerification class do external callout to Smarty Street address API. External callout code is available in another blog Generic Apex class for Calling External System. To store API detail, I have used named credential SmartyStreet which will store API url https://us-street.api.smartystreets.com/street-address , username and password.
AddressValidationQueueable is child queueable class which will call AddressVerification class for each record and add those accounts in list. Once set of 99 records are processed, we will update those records and start new child queueable class to process next set of 99 records. We are also removing that record from list so that it will not process again.
Why 99 records in one batch?
As we can call only 100 records in one batch in asynchronous apex. I have used 99 records instead of 100 but you can use 100 as well.
- Generic Apex class for Calling External System
- Revisit Asynchronous Apex : Type and Usage
- Queueable Apex
- Smarty Street API
If you need more discussion on this topic, let us connect for discussing and implementing this pattern.
[…] Avoid Batch Apex and Use Queueable Class […]
Hello thanks for a great solution,
I tried this yesterday and it all works fine with above 200 records, suddenly I started to get this error
Too many queueable jobs added to the queue: 2
Any Idea why this happened ?
This error will come when you are creating many queueable parent jobs. We should create new queueable job from child queueable class like i have mentioned in AddressValidationQueueable.apxc lines #48-50. I am not creating new job from AddressValidationQueueableRunner, this is done in AddressValidationQueueable.
Let me know if this is not working.
[…] blog Avoid Batch Apex and Use Queueable Class to understand where we shoudl use Queueable instead of Batch […]
Question. Why call processTargets(accts) twice? Why not call if before the the If statement? Seems like an issue to concise coding. Unless I’m missing something?
Thank You, Wallace. It should be called only once before queuing a new job. I have updated the code.