I am looking for ways to speed up a complex Batch job which takes days to run in a specific customer org.
This Batch is snapshoting records of arbitrary objects into denormalized (as a single records contains fields of multiple related objects) "backup" object.
public Database.QueryLocator start(Database.BatchableContext context) {
String complexSoql = 'SELECT a,b,c,..z (SELECT a,b,c,..z FROM Childs__r...), (SELECT...';
return Database.getQueryLocator(complexSoql);
}
public void execute(Database.BatchableContext context, List<SObject> scope) {
List<Snapshot__c> snapshots = new List<Snapshot__c>();
for(SObject sourceRecord : scope ) {
snapshots.add( createSnapshot(sourceRecord) );
}
insert snapshots;
...
Currently in the start() we query all of the source fields including those of related objects. This can sometimes be dozens up to hundreds of fields. Starting the batch currently can take more than 10 minutes and each Batch job (a single call of execute()) yields a big 15 second delay on the Server describes here in more detail.
We ran into many hard to reproduce or fix errors. Some of them documented here:
- Apex CPU Time limit in Batch start()
- Batch execute is slow because of huge unused gaps in DevConsole timeline
Now the idea came up if one could speed up things by dramatically simplifying the start() query. Select only the Id field and get rid of all the sub-selects. Instead we query the full objects per execute().
public Database.QueryLocator start(Database.BatchableContext context) {
String simpleSoql = 'SELECT Id FROM...";
return Database.getQueryLocator(simpleSoql);
}
public void execute(Database.BatchableContext context, List<SObject> scope) {
String soql = 'SELECT many fields, (SELECT many fields FROM...)... WHERE Id IN :scope';
for(SObject sourceRecord : Database.query(soql) ) {
...
Would this speed things up and why? Are there any other best practices to make Batch classes run faster?