I have an ever-evolving set of custom rollups from Tasks to Accounts, Contacts, and Leads. When I add a new rollup to the code, sometimes I need to calculate the value for all parent records.
I've written a pair of Batch classes that should be able to do this for me. RecalculateTaskRollupBatch
queries my entire task object (>2m records) and builds a set of one task Id for each parent Account, Lead, and Contact. My goal is to then pass this Id set to another batch class, UpdateTaskBatch
that simply updates those ids, which will fire the trigger that calculates the rollup values on the parent records.
I've gotten RecalculateTaskRollupBatch
to run just fine, and it chugs through >12k batches of 200 tasks in about an hour. But at the end of its run, it doesn't launch the UpdateTasksBatch
.
I've invoked Database.Stateful
, but is there something I'm doing wrong here?
global class RecalculateTaskRollupBatch implements Database.Batchable<sObject>, Database.Stateful
{
global final String query;
global final Integer batchSize;
global static Set<Id> parentIds = new Set<Id>();
global static Set<Id> tasksToUpdate = new Set<Id>();
global RecalculateTaskRollupBatch(Integer updateBatchSize)
{
batchSize = updateBatchSize;
query = 'SELECT Id, AccountId, WhoId FROM Task'; //ALL TASKS!
}
global Database.QueryLocator start(Database.BatchableContext BC)
{
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<Task> scope)
{
// Only add one task per parent to tasksToUpdate
for(Task t : scope)
{
if(t.AccountId != NULL && !parentIds.contains(t.AccountId))
{
tasksToUpdate.add(t.id);
parentIds.add(t.AccountId);
}
if(t.WhoId != NULL && !parentIds.contains(t.WhoId))
{
if(!tasksToUpdate.contains(t.id)) tasksToUpdate.add(t.id);
parentIds.add(t.WhoId);
}
}
}
global void finish(Database.BatchableContext BC)
{
if(tasksToUpdate.size() > 0)
{
Database.executeBatch(new UpdateTasksBatch(tasksToUpdate),batchSize);
}
}
}
Here is the second batch class:
global class UpdateTasksBatch implements Database.Batchable<sObject> {
String query;
Set<Id> tasksToUpdate;
global UpdateTasksBatch(Set<Id> tasksPassed) {
tasksToUpdate = tasksPassed;
query = 'SELECT Id FROM Task WHERE Id IN :tasksToUpdate';
}
global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<Task> scope) {
update scope;
}
global void finish(Database.BatchableContext BC) {
/*
Eventually I would love to keep track of failed batches and retry them
*/
}
}