7

I have a batch program that works in most scenarios, but if the number of leads is too many, I get the error

"heap size is too large"

I've tried removing elements from my list after they have been used and I'm using the soql for loop to reduce heap size, but I still hit the error when there are too many leads getting processed. Is there any other ways I can reduce the heap size in my batch code?

global class LeadExperianMatchingBatch implements Database.Batchable<sObject> {

BCBSAZSettings__c settings = BCBSAZSettings__c.getInstance();
global Integer lastNdays = Integer.valueOf(settings.LeadLastNDays__c);
global String query = 'Select Name, Date_of_Birth_Primary_Year_Month__c, Street_Number__c, ZipCode5__c from Lead where Experian_Priority__c = false AND Experian_Bypass__c = false AND Is_IU65__c = true AND CreatedDate = LAST_N_DAYS: ' + lastNdays + ' AND Name != null AND (Street_Number__c != \'X\' OR ZipCode5__c != NULL OR Date_of_Birth_Primary_Year_Month__c != \'0\')';
global Map<String, Experian__c> experianMap = new Map<String, Experian__c>();

global database.querylocator start(Database.BatchableContext BC)
{
    return Database.getQueryLocator(query);
}

global void execute(Database.BatchableContext BC, List<sObject> scope)
{   
    getExperianMaps();
    List<Lead> leadsToUpdate = new List<Lead>();
    for (Lead l : (List<Lead>)scope) {
        if(!experianMap.isEmpty() && experianMap.size() > 0) {
            if(experianMap.containsKey(l.Name.toLowercase())) {
                if(experianMap.get(l.Name.toLowercase()) != null) {
                    if(experianMap.get(l.Name.toLowercase()).Primary_Address__c != null) {
                        if(experianMap.get(l.Name.toLowercase()).Primary_Address__c.startsWith(l.Street_Number__c)) {
                            l.Experian_Priority__c = true;
                            l.Experian_Name__c = experianMap.get(l.Name.toLowercase()).Id;
                            leadsToUpdate.add(l);
                        }
                    }
                    else if(experianMap.get(l.Name.toLowercase()).Date_of_Birth__c != null) {
                        if(experianMap.get(l.Name.toLowercase()).Date_of_Birth__c == l.Date_of_Birth_Primary_Year_Month__c) {
                            l.Experian_Priority__c = true;
                            l.Experian_Name__c = experianMap.get(l.Name.toLowercase()).Id;
                            leadsToUpdate.add(l);                   
                        }
                    }
                    else if(experianMap.get(l.Name.toLowercase()).Zip_Code__c != null) {
                        if(experianMap.get(l.Name.toLowercase()).Zip_Code__c == l.ZipCode5__c) {
                            l.Experian_Priority__c = true;
                            l.Experian_Name__c = experianMap.get(l.Name.toLowercase()).Id;
                            leadsToUpdate.add(l);                   
                        }
                    }
                }
                experianMap.remove(l.Name.toLowercase());
            }
        }   
    }

    if(!leadsToUpdate.isEmpty() && leadsToUpdate.size() > 0) {
        List<Database.Saveresult> results = Database.update(leadsToUpdate, false);
        for(Database.SaveResult sr : results) {
            if(!sr.isSuccess()) {
                for(Database.Error err : sr.getErrors()) {
                    // process any errors here 
                    system.debug(err.getMessage());
                    system.debug(err.getStatusCode());
                }                   
            }
        }
    }
}

global void finish(Database.BatchableContext BC) {

}

private void getExperianMaps() {
    for(Experian__c ex : [select Id, Name, Primary_Address__c, Date_of_Birth__c, Zip_Code__c from Experian__c]) {
        experianMap.put(ex.Name.toLowercase(), ex);
    }
}
}
  • 1
    How many Lead records does it take to get this error? Also, how big is the Experian__c table? – Nick Cook Sep 7 '15 at 1:54
1

The first (crude) answer is tune down your batch size.

However, as suggested, if Experian__c has many records, then this will be a fixable problem. Since you only want the ones that are mentioned within the scope, you could collect the relevant names from the Leads in the scope first, and then use that set to filter your query down i.e.

global void execute(Database.BatchableContext BC, List<sObject> scope)
{   
    getExperianMaps((List<Lead>)scope);
    List<Lead> leadsToUpdate = new List<Lead>();
    for (Lead l : (List<Lead>)scope) {
        // Same as before...
    }
}

private void getExperianMaps(List<Lead> leads) {
    Set<String> leadNames = new Set<String>();
    for(Lead l : leads) {
        // Don't care about case here as the SOQL comparison is case-insensitive
        leadNames.add(l.Name);
    }
    for(Experian__c ex : [select Id, Name, Primary_Address__c, Date_of_Birth__c, Zip_Code__c from Experian__c where Name IN :leadNames]) {
        experianMap.put(ex.Name.toLowercase(), ex);
    }
}
0

I wonder if the problem is that you are doing the query to populate the experianMap on each execution of the batch. What happens if you make the class stateful and just perform the getExperianMaps() method once in the batch start()?

global class LeadExperianMatchingBatch implements Database.Batchable<sObject>, Database.Stateful {

BCBSAZSettings__c settings = BCBSAZSettings__c.getInstance();
global Integer lastNdays = Integer.valueOf(settings.LeadLastNDays__c);
global String query = 'Select Name, Date_of_Birth_Primary_Year_Month__c, Street_Number__c, ZipCode5__c from Lead where Experian_Priority__c = false AND Experian_Bypass__c = false AND Is_IU65__c = true AND CreatedDate = LAST_N_DAYS: ' + lastNdays + ' AND Name != null AND (Street_Number__c != \'X\' OR ZipCode5__c != NULL OR Date_of_Birth_Primary_Year_Month__c != \'0\')';
global Map<String, Experian__c> experianMap = new Map<String, Experian__c>();

global database.querylocator start(Database.BatchableContext BC)
{
    getExperianMaps();
    return Database.getQueryLocator(query);
}

global void execute(Database.BatchableContext BC, List<sObject> scope)
{   
    List<Lead> leadsToUpdate = new List<Lead>();
    for (Lead l : (List<Lead>)scope) {
        if(!experianMap.isEmpty() && experianMap.size() > 0) {
            if(experianMap.containsKey(l.Name.toLowercase())) {
                if(experianMap.get(l.Name.toLowercase()) != null) {
                    if(experianMap.get(l.Name.toLowercase()).Primary_Address__c != null) {
                        if(experianMap.get(l.Name.toLowercase()).Primary_Address__c.startsWith(l.Street_Number__c)) {
                            l.Experian_Priority__c = true;
                            l.Experian_Name__c = experianMap.get(l.Name.toLowercase()).Id;
                            leadsToUpdate.add(l);
                        }
                    }
                    else if(experianMap.get(l.Name.toLowercase()).Date_of_Birth__c != null) {
                        if(experianMap.get(l.Name.toLowercase()).Date_of_Birth__c == l.Date_of_Birth_Primary_Year_Month__c) {
                            l.Experian_Priority__c = true;
                            l.Experian_Name__c = experianMap.get(l.Name.toLowercase()).Id;
                            leadsToUpdate.add(l);                   
                        }
                    }
                    else if(experianMap.get(l.Name.toLowercase()).Zip_Code__c != null) {
                        if(experianMap.get(l.Name.toLowercase()).Zip_Code__c == l.ZipCode5__c) {
                            l.Experian_Priority__c = true;
                            l.Experian_Name__c = experianMap.get(l.Name.toLowercase()).Id;
                            leadsToUpdate.add(l);                   
                        }
                    }
                }
                experianMap.remove(l.Name.toLowercase());
            }
        }   
    }

    if(!leadsToUpdate.isEmpty() && leadsToUpdate.size() > 0) {
        List<Database.Saveresult> results = Database.update(leadsToUpdate, false);
        for(Database.SaveResult sr : results) {
            if(!sr.isSuccess()) {
                for(Database.Error err : sr.getErrors()) {
                    // process any errors here 
                    system.debug(err.getMessage());
                    system.debug(err.getStatusCode());
                }                   
            }
        }
    }
}

global void finish(Database.BatchableContext BC) {

}

private void getExperianMaps() {
    for(Experian__c ex : [select Id, Name, Primary_Address__c, Date_of_Birth__c, Zip_Code__c from Experian__c]) {
        experianMap.put(ex.Name.toLowercase(), ex);
    }
}
}

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.