0

I am receiving the following error, when trying to bulk load the customer records. Some of them are uploaded successfully, while some of them are throwing the following error.

<message>
AccountTrigger: System.LimitException: Apex CPU time limit exceeded
</message>
<statusCode>CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY</statusCode>

Please find the status shown in debug logs below:

  Number of SOQL queries: 1 out of 100
  Number of query rows: 50000 out of 50000 ******* CLOSE TO LIMIT
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Maximum CPU time: 15026 out of 10000 ******* CLOSE TO LIMIT
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

There are around 800,000 existing records in the database. I think when the query in trigger is trying to match the existing records, it is throwing exception.

My question is in situations like this, we should go with Batch Apex right?

If there is any other work around, Please suggest.

Please find my trigger logic below:

public class AccountInsertTriggerHandler {
private List<Account> trgOldList = new List<Account> ();
private List<Account> trgNewList = new List<Account> ();
private Map<id,Account> trgOldMap = new Map<id,Account>();
private Map<id,Account> trgNewMap = new Map<id,Account>();
public Map<String,Account> leadsMap = new Map<String,Account>();
private List<String> LeadList = new List<String> ();

public AccountInsertTriggerHandler(List<Account> trgOldList,List<Account> trgNewList,Map<id,Account> trgOldMap,Map<id,Account> trgNewMap){
    this.trgOldList = trgOldList;
    this.trgNewList = trgNewList;
    this.trgOldMap = trgOldMap;
    this.trgNewMap = trgNewMap;

    for(Account acct: trgNewList){
        String UniqueCustID = acct.Region__pc + '-' + acct.PersonEmail;
        LeadList.add(UniqueCustID);
    }
    List<Account> allLeads = [select AP_21_Per_IDX__c, AP21_perIDx__pc, AP21_Code__pc, Salutation, AP21_Initials__pc, 
                                FirstName, LastName, Gender__pc, PersonBirthdate, AP21_StartDate__pc, PersonTitle, AP21_Password__pc,
                                 AP21_UpdateTimestamp__pc, AP21_Privacy__pc, AP21_Reference_Sales_rep__pc, BillingStreet, 
                                 AP21_Billing_AddressLine2__pc, BillingCity, BillingState, BillingPostalCode, BillingCountry, 
                                 ShippingStreet, ShippingCity, ShippingState, ShippingPostalCode, 
                                 ShippingCountry, PersonEmail, Home_Phone__pc, PersonMobilePhone, Business_Phone__pc, 
                                 AP21_Loyalty_ID__pc, AP21_LoyaltyTypeID__pc, AP21_LoyaltyType__pc, AP21_Loyalty_CardNo__pc, 
                                 AP21_Loyalty_CardExpiry__pc, AP21_Loyalty_Balance__pc, AP21_Loyalty_CreditStatus__pc, 
                                 AP21_Loyalty_Message__pc, AP21_Loyalty_JoinDate__pc, AP21_LoyaltyStatusID__pc, 
                                 AP21_Loyalty_Currency__pc, AP21_Loyalty_Code__pc, AP21_Loyalty_Format__pc, Store__pc, Region__pc
                                 from Account where AP_21_Per_IDX__c = null and UniqueID__pc IN : LeadList];
    for (Account accLeads: allLeads){
        String UniqueLeadsID = accLeads.Region__pc + '-' + accLeads.PersonEmail;
        leadsMap.put(UniqueLeadsId,accLeads);
        System.debug(leadsMap);
    }
}
public void OnAfterInsert(){
    CompareCustomersToLeads(trgNewList,null);
}
/*public void OnBeforeInsert(){
    CompareCustomersToLeads(trgNewList,null);
}*/
public void CompareCustomersToLeads(List<Account> trgNewList,Map<id,Account> trgOldMap){
    //Add to map using Region and emailAddress as Key and Account record as value.Only add records from Flow User
    ////Retrieve all leads (where PerIdx is blank and created by not Flow) and create map
    ////compare similiar records in the maps i.e for each customer record in customer map, find lead record
    //compare values and update lead with PerIdx and other values
    Map<String,Account> customerMap = new Map<String,Account>();
   // Map<String,Account> leadsMap = new Map<String,Account>();
    List<Account> leadsToUpdate = new List<Account>();
    List<Account> customersToDelete = new List<Account>();
    for(Account acc: trgNewList){
        String UniqueID = acc.Region__pc + '-' + acc.PersonEmail;
        if(leadsMap.containsKey(UniqueID)){
            customerMap.put(UniqueID,acc);
            //acc.addError('No Match to Lead hence cannot Insert');
        }

        System.debug(customerMap);

    }
    for(String key:customerMap.keySet()){
        System.debug('********KEY ' + key);
        if(leadsMap.containsKey(key)){
            Account customerRecord = customerMap.get(key);
            Account leadsRecord = leadsMap.get(key);
            System.debug(customerRecord);
            System.debug(leadsRecord);

            if(customerRecord.AP_21_Per_IDX__c!=null &&leadsRecord.AP_21_Per_IDX__c==null){
                System.debug('*********** Met Conditions');
                leadsRecord.AP_21_Per_IDX__c = customerRecord.AP_21_Per_IDX__c;
                leadsRecord.AP21_perIDx__pc = customerRecord.AP21_perIDx__pc;
                leadsRecord.FirstName = customerRecord.FirstName;
                leadsRecord.LastName = customerRecord.LastName;
                leadsRecord.PersonMobilePhone = customerRecord.PersonMobilePhone;
                if(leadsRecord.Gender__pc==null)
                    leadsRecord.Gender__pc = customerRecord.Gender__pc;
                if(leadsRecord.PersonBirthdate==null)
                    leadsRecord.PersonBirthdate = customerRecord.PersonBirthdate;
                if(leadsRecord.Store__pc==null)
                    leadsRecord.Store__pc = customerRecord.Store__pc;
                leadsRecord.AP21_Code__pc = customerRecord.AP21_Code__pc;
                leadsRecord.Salutation = customerRecord.Salutation;
                leadsRecord.AP21_Initials__pc = customerRecord.AP21_Initials__pc;
                leadsRecord.AP21_StartDate__pc = customerRecord.AP21_StartDate__pc;

                leadsRecord.PersonTitle = customerRecord.PersonTitle;
                leadsRecord.AP21_Password__pc = customerRecord.AP21_Password__pc;
                leadsRecord.AP21_UpdateTimestamp__pc = customerRecord.AP21_UpdateTimestamp__pc;
                leadsRecord.AP21_Privacy__pc = customerRecord.AP21_Privacy__pc;
                leadsRecord.AP21_Reference_Sales_rep__pc = customerRecord.AP21_Reference_Sales_rep__pc;
                leadsRecord.BillingStreet = customerRecord.BillingStreet;
                leadsRecord.AP21_Billing_AddressLine2__pc = customerRecord.AP21_Billing_AddressLine2__pc;

                leadsRecord.BillingCity = customerRecord.BillingCity;
                leadsRecord.BillingState = customerRecord.BillingState;
                leadsRecord.BillingPostalCode = customerRecord.BillingPostalCode;
                leadsRecord.BillingCountry = customerRecord.BillingCountry;

                leadsRecord.ShippingStreet = customerRecord.ShippingStreet;
                leadsRecord.ShippingCity = customerRecord.ShippingCity;
                leadsRecord.ShippingState = customerRecord.ShippingState;
                leadsRecord.ShippingPostalCode = customerRecord.ShippingPostalCode;
                leadsRecord.ShippingCountry = customerRecord.ShippingCountry;
                leadsRecord.PersonEmail = customerRecord.PersonEmail;
                if(leadsRecord.Home_Phone__pc==null)
                 leadsRecord.Home_Phone__pc = customerRecord.Home_Phone__pc;
                if(leadsRecord.PersonMobilePhone==null)
                 leadsRecord.PersonMobilePhone = customerRecord.PersonMobilePhone;
                if(leadsRecord.Business_Phone__pc==null)
                 leadsRecord.Business_Phone__pc = customerRecord.Business_Phone__pc;



                leadsRecord.AP21_Loyalty_ID__pc = customerRecord.AP21_Loyalty_ID__pc;
                leadsRecord.AP21_LoyaltyTypeID__pc = customerRecord.AP21_LoyaltyTypeID__pc;
                leadsRecord.AP21_LoyaltyType__pc = customerRecord.AP21_LoyaltyType__pc;
                leadsRecord.AP21_Loyalty_CardNo__pc = customerRecord.AP21_Loyalty_CardNo__pc;
                leadsRecord.AP21_Loyalty_CardExpiry__pc = customerRecord.AP21_Loyalty_CardExpiry__pc;
                leadsRecord.AP21_Loyalty_Balance__pc = customerRecord.AP21_Loyalty_Balance__pc;
                leadsRecord.AP21_Loyalty_CreditStatus__pc = customerRecord.AP21_Loyalty_CreditStatus__pc;

                leadsRecord.AP21_Loyalty_Message__pc = customerRecord.AP21_Loyalty_Message__pc;
                leadsRecord.AP21_Loyalty_JoinDate__pc = customerRecord.AP21_Loyalty_JoinDate__pc;
                leadsRecord.AP21_LoyaltyStatusID__pc = customerRecord.AP21_LoyaltyStatusID__pc;
                leadsRecord.AP21_Loyalty_Currency__pc = customerRecord.AP21_Loyalty_Currency__pc;
                leadsRecord.AP21_Loyalty_Code__pc = customerRecord.AP21_Loyalty_Code__pc;
                leadsRecord.AP21_Loyalty_Format__pc = customerRecord.AP21_Loyalty_Format__pc;
                leadsRecord.Region__pc = customerRecord.Region__pc;




                leadsToUpdate.add(leadsRecord);

                customersToDelete.add(customerRecord);
            }

        }
    }
    update leadsToUpdate;
    List<Account> deleteCustomers = [select id from Account where id IN: customersToDelete];
    delete deleteCustomers;

}

}

Please find the debug log after removing the debug statements in production code:

10:09:58.714 (18562455772)|SOQL_EXECUTE_BEGIN|[152]|Aggregations:0|SELECT id FROM Account WHERE id IN :tmpVar1 LIMIT 50000

10:09:58.714 (18569696442)|SOQL_EXECUTE_END|[152]|Rows:1 10:09:58.714 (18569734502)|EXCEPTION_THROWN|[152]|System.LimitException: Too many query rows: 50001 10:09:58.714 (18569853312)|HEAP_ALLOCATE|[152]|Bytes:30 10:09:58.714 (18569880194)|METHOD_EXIT|[47]|01p7F00000O4xqM|AccountInsertTriggerHandler.CompareCustomersToLeads(List, Map) 10:09:58.714 (18569890597)|METHOD_EXIT|[4]|01p7F00000O4xqM|AccountInsertTriggerHandler.OnAfterInsert() 10:09:58.714 (18569947597)|FATAL_ERROR|System.LimitException: Too many query rows: 50001

Class.AccountInsertTriggerHandler.CompareCustomersToLeads: line 152, column 1 Class.AccountInsertTriggerHandler.OnAfterInsert: line 47, column 1 Trigger.AccountTrigger: line 4, column 1 10:09:58.714 (18569960836)|FATAL_ERROR|System.LimitException: Too many query rows: 50001

Class.AccountInsertTriggerHandler.CompareCustomersToLeads: line 152, column 1 Class.AccountInsertTriggerHandler.OnAfterInsert: line 47, column 1 Trigger.AccountTrigger: line 4, column 1 10:09:59.570 (18570083066)|CUMULATIVE_LIMIT_USAGE 10:09:59.570 (18570083066)|LIMIT_USAGE_FOR_NS|(default)| Number of SOQL queries: 2 out of 100 Number of query rows: 50001 out of 50000 ******* CLOSE TO LIMIT Number of SOSL queries: 0 out of 20 Number of DML statements: 1 out of 150 Number of DML rows: 3 out of 10000 Maximum CPU time: 5296 out of 10000 ******* CLOSE TO LIMIT Maximum heap size: 0 out of 6000000 Number of callouts: 0 out of 100 Number of Email Invocations: 0 out of 10 Number of future calls: 0 out of 50 Number of queueable jobs added to the queue: 0 out of 50 Number of Mobile Apex push calls: 0 out of 10

10:09:59.570 (18570083066)|CUMULATIVE_LIMIT_USAGE_END

10:09:58.714 (18571196926)|CODE_UNIT_FINISHED|AccountTrigger on Account trigger event AfterInsert|__sfdc_trigger/AccountTrigger 10:09:58.714 (18572303315)|EXECUTION_FINISHED

1 Answer 1

2

Your debug statements are sucking the performance right out of your code. Never leave debug statements in production code. And I do mean never. Let me give you a trivial example.

Map<Integer, Integer> values = new Map<Integer, Integer>();
for(Integer i = 0; i < 7500; i++) {
    values.put(i, i);
    system.debug('item'+values);
}

This code tries to put 7,500 items into a map. It requires about 9.7 seconds to run:

  Number of SOQL queries: 0 out of 100
  Number of query rows: 0 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Maximum CPU time: 9714 out of 10000 ******* CLOSE TO LIMIT
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

Now, we remove the debug statement:

  Number of SOQL queries: 0 out of 100
  Number of query rows: 0 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

The script took so little time it didn't even register in the governor limits. The exact same code minus debug statements. Now, I'm not saying that this will fix your issue, but considering that you're using debug statements in every single loop, I can pretty much guarantee it'll save at least 5 seconds of CPU time, if not more.

If you find yourself in a position where you need to debug, use checkpoints instead. They will give you a complete memory graph at any arbitrary point of execution, and when you're done, you can disable them so they don't take up tons of CPU time.

11
  • How to handle when you have to debug in prod? Aug 19, 2018 at 8:22
  • @PranayJaiswal Use checkpoints.
    – sfdcfox
    Aug 19, 2018 at 8:26
  • This is not apex active debugger right? It hardly works if you have too many batches nd queable queued Aug 19, 2018 at 8:36
  • @sfdcfox I will remove the debug statements, and will check the performance. Thanks for teaching about that. Just one question. If we want to write a logic, where we have to check the existing records in database (Say some 800,00), which is the best way to go? The thing that comes to my mind is to Write a batch apex and schedule it to run at some time. Please let me know if you have anything to add to this
    – Raj
    Aug 19, 2018 at 9:31
  • @PranayJaiswal Correct. Debug statements in general are performance killers and must be avoided in production code at all costs. We're not talking about the debugger; if you need to use it, you're already in a sandbox and can see all data in memory and the current line of execution.
    – sfdcfox
    Aug 19, 2018 at 13:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .