3

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

}
1

Ok, so I kept on trying stuff and fixed the problem by removing the static modifier from my global id sets. The start of RecalculateTaskRollupBatch now looks like this (and it works):

global class RecalculateTaskRollupBatch implements Database.Batchable<sObject>, Database.Stateful 
{   
    global final String query;
    global final Integer batchSize;
    global Set<Id> parentIds = new Set<Id>();
    global Set<Id> tasksToUpdate = new Set<Id>();

    global RecalculateTaskRollupBatch(Integer updateBatchSize) 
    {   
....

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.