function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Ramk123Ramk123 

Trigger System Limit Exception: Apex CPU time limit exceeded

Dear Experts,

I am getting "System Limit Exception: Apex CPU time limit exceeded" on my trigger. Kindly help me to correct the logic. Thank you very much for your quick help!

Trigger
trigger ContactTrigger on Contact (before insert, after insert, before update, after update) { 
    
    if(Trigger.isBefore && ContactTriggerHandler.runContactTrigger){
        if(Trigger.isInsert || Trigger.isUpdate){
            ContactTriggerHandler.validateAliedRegistrationNumbers(Trigger.new);
            ContactTriggerHandler.validateAClientMDHHSNumbers(Trigger.new);
            ContactTriggerHandler.setContactAccountByCaseIds(Trigger.new);
        }
    }
    if(Trigger.isAfter && ContactTriggerHandler.runContactTrigger){
        
        if(Trigger.isInsert){
            ContactTriggerHandler.getHouseholdAcctsForSharing(Trigger.new, Trigger.newMap);
            ContactTriggerHandler.runContactTrigger = false;
            ContactTriggerHandler.insertSubsidiaryAccounts(Trigger.new);
        }
        if(Trigger.isUpdate){
            ContactTriggerHandler.runContactTrigger = false;
        }
    }

Trigger Handler
 
public class ContactTriggerHandler {

    public static Boolean runContactTrigger = true;
    
    public static Id accountHouseholdRT = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Household').getRecordTypeId();
    public static Id accountOrganizationRT = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Organization').getRecordTypeId();
    public static Id accountVolunteerRT = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Volunteer_Household').getRecordTypeId();
    public static Id contactProviderRT = Schema.SObjectType.Contact.getRecordTypeInfosByDeveloperName().get('Provider').getRecordTypeId();
    public static Id contactClientRT = Schema.SObjectType.Contact.getRecordTypeInfosByDeveloperName().get('Client').getRecordTypeId();
    public static Id contactURMRT = Schema.SObjectType.Contact.getRecordTypeInfosByDeveloperName().get('URM').getRecordTypeId();
    
    //Method: getHouseholdAcctsForSharing
    //Desc:   When a new Provider rType Contact is created, they need to be granted access to all Household Accounts that all Agency employees from their Agency 
    //        have access to. If new provider is part of an agency, that is a parent agency to other agencies (top level account), then they should also have access
    //        to all child agency related Household Accounts 
    public static void getHouseholdAcctsForSharing(List<Contact> newContacts, Map<Id, Contact> newMap){
        
        List<AccountContactRelation> relatedHouseholdAccts = new List<AccountContactRelation>();
        Map<Id,List<Id>> householdIdAgencyAll = new Map<Id,List<Id>>();
        Set<Id> providerAgencyIds = new Set<Id>();
        Set<Id> agencyContacts = new Set<Id>();
        Map<Id, List<Id>> agencyWithEmployees = new Map<Id, List<Id>>();
        Map<Id, List<Id>> agencyEmpWithHouseholdIds = new Map<Id, List<Id>>();
                
        for(Contact c : newContacts){
            
            if(c.RecordTypeId == contactProviderRT){
                providerAgencyIds.add(c.AccountId);
            }
        }
        
        system.debug(providerAgencyIds.size());
        
        if(providerAgencyIds.size()>0){
            //Get URM & Provider contacts related to agency of new employee
            List<AccountContactRelation> relatedAgencyEmployees = [SELECT Id, AccountId, ContactId, Roles 
                                                                   FROM AccountContactRelation WHERE AccountId IN:providerAgencyIds
                                                                   AND Contact.RecordTypeId =:contactProviderRT];
            
            system.debug(relatedAgencyEmployees.size());
            
            List<Id> agencyEmpIds = null;
            
            for(AccountContactRelation acr : relatedAgencyEmployees){
                
                agencyContacts.add(acr.ContactId);
                agencyEmpIds = agencyWithEmployees.get(acr.AccountId);
                
                if(agencyEmpIds == null){
                    agencyEmpIds = new List<Id>();
                }
                agencyEmpIds.add(acr.ContactId);
                agencyWithEmployees.put(acr.AccountId, agencyEmpIds);
            }
            
            if(agencyWithEmployees.size()>0){
                
                //Get all household accts that agency contacts have access to for assignment to new employee 
                relatedHouseholdAccts = [SELECT Id, AccountId, ContactId, Roles FROM AccountContactRelation 
                                         WHERE ContactId IN:agencyContacts AND (Account.RecordTypeId =:accountHouseholdRT OR Account.RecordTypeId =:accountVolunteerRT)];

                List<Id> householdIds = null;
                
                for(AccountContactRelation acr : relatedHouseholdAccts){
                    
                    householdIds = agencyEmpWithHouseholdIds.get(acr.ContactId);
                    
                    if(householdIds == null){
                        householdIds = new List<Id>();
                    }
                    householdIds.add(acr.AccountId);
                    agencyEmpWithHouseholdIds.put(acr.ContactId, householdIds);
                }

                List<Id> hIds = null;
                //agency Id
                for(Id aId : agencyWithEmployees.keyset()){
                    //employee contact Ids
                    for(Id eId : agencyWithEmployees.get(aId)){
                        
                        if(agencyEmpWithHouseholdIds.keyset().contains(eId)){
                            
                            List<Id> relatedHIDs = agencyEmpWithHouseholdIds.get(eId);
                            system.debug(relatedHIDS.size());
                            hIds = householdIdAgencyAll.get(aId);
                            
                            if(hIds == null){
                                hIds = new List<Id>();
                            }
                            for(Id rHids : relatedHIDS){
                                hIds.add(rHids);
                            }
                            
                            system.debug(hIds.size());
                            householdIdAgencyAll.put(aId, hIds);
                        }
                        
                    }
                }

                List<AccountContactRelation> householdRelationshipsToCreate = new List<AccountContactRelation>();
                Map<Id, AccountContactRelation> householdRelationshipMap = new Map<Id, AccountContactRelation>();
                Map<Id, Set<Id>> agencyContactWithHouseholdIds = new Map<Id, Set<Id>>();
                List<Id> hhIds = null;
                Set<Id> hhSet = new Set<Id>();
                
                for(Contact c : newContacts){
                    hhIds = householdIdAgencyAll.get(c.AccountId);
                    
                    if(hhIds != null){
                        hhSet.addAll(hhIds);
                        agencyContactWithHouseholdIds.put(c.id, hhSet);     
                    }
                }
                
                if(agencyContactWithHouseholdIds.size()>0){
                    
                    for(Id cId : agencyContactWithHouseholdIds.keyset()){
                        
                        for(Id hhId : agencyContactWithHouseholdIds.get(cId)){
                            
                            AccountContactRelation acr = new AccountContactRelation();
                            acr.ContactId = cId;
                            acr.AccountId = hhId;
                            acr.IsActive = true;
                            acr.Roles = 'Agency';
                            householdRelationshipsToCreate.add(acr);
                        }
                    }
                }

                if(householdRelationshipsToCreate.size()>0){
                    insert householdRelationshipsToCreate;
                }
            }
        }
    }

    public static void validateAliedRegistrationNumbers(List<Contact> newRecords){
        
        for(Contact c : newRecords){
            
            if(c.Alien_Number__c != null && c.Alien_Registration_Number_DW__c == null){
                c.Alien_Registration_Number_DW__c = c.Alien_Number__c;
            } 
            
            else if(c.Alien_Registration_Number_DW__c != null && c.Alien_Number__c == null ){
                c.Alien_Number__c = c.Alien_Registration_Number_DW__c;
            }

            if(c.Alien_Number__c != null){
                string aliennumber = c.Alien_Number__c;
                c.Alien_Number__c = aliennumber.replaceAll('[^0-9]', '');
            }

            if(c.Alien_Registration_Number_DW__c != null ){
                string aliennumber = c.Alien_Registration_Number_DW__c;
                c.Alien_Registration_Number_DW__c = aliennumber.replaceAll('[^0-9]', '');
            }

        }
    }
    public static void validateAClientMDHHSNumbers(List<Contact> newRecords){
        
        for(Contact c : newRecords){

            if(c.MD_ID__c != null && c.Client_MD_ID__c == null){
                c.Client_MD_ID__c = c.MD_ID__c;
            } else if(c.Client_MD_ID__c != null && c.MD_ID__c == null ){
                c.MD_ID__c = c.Client_MD_ID__c;
            }
        }
    }

    public static void setContactAccountByCaseIds(List<Contact> newRecords){
        Set<String> caseIds = new Set<String>();
        Map<String,Id> caseIDMap = new Map<String,Id>();

        for(Contact c : newRecords){

            if(c.MD_ID__c != null && c.Case_ID__c != null){
                caseIds.add(c.Case_ID__c);
            } 
        }
        List<Account> accounts = [SELECT Id, Case_ID__c FROM Account WHERE Case_ID__c in: caseIDs];
        for(Account a : accounts){
            caseIDMap.put(a.Case_ID__c, a.Id);
        }
        for(Contact c : newRecords){
            if(c.MD_ID__c != null && c.Case_ID__c != null && c.AccountId == null && caseIDMap.containsKey(c.Case_ID__c)){
                c.accountId = caseIDMap.get(c.Case_ID__c);
            } 
        }
    }
    
    public static void insertSubsidiaryAccounts(List<Contact> newRecords) {
        
        for(Contact ct : newRecords) {
            if(ct.RecordTypeId == contactProviderRT) {
                AccountRelationService.insertSubsidiaries(ct);
            }
        }
        
        if(AccountRelationService.relatedContacts.size() > 0) {
            Database.insert(AccountRelationService.relatedContacts, false);
        }
    }

 
VinayVinay (Salesforce Developers) 
It would be difficult to tell why you see CPU time limit exception.  Debug logs is the best way to narrow down the scenerio and check the issue.

Check below references for check point details.

https://help.salesforce.com/s/articleView?id=000339361&type=1
https://www.salesforceben.com/what-is-apex-cpu-time-limit-exceeded-how-do-you-solve-it/

Please mark as Best Answer if above information was helpful.

Thanks,
MenteeMentee
I agree with Vinay. Logs are best place to find solution. 
I came across such scenario and I was able to use @ future method to perform some updates asynchronously. Let me know if you still need help.
Ramk123Ramk123
Hi Vinay/Mentee.

I am sorry for the late response!

Here is the stack tree.

User-added image
I think the issue is with the following highlighted code. Kindly suggest the code to avoid the Apex CPU time limit?
public class AccountContactRelationTriggerHandler {
    
    public static Id accountHouseholdRT = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Household').getRecordTypeId();
    public static Id accountVolunteerRT = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Volunteer_Household').getRecordTypeId();
    public static Id accountOrganizationRT = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Organization').getRecordTypeId();
    public static Id contactProviderRT = Schema.SObjectType.Contact.getRecordTypeInfosByDeveloperName().get('Provider').getRecordTypeId();
    public static Id contactClientRT = Schema.SObjectType.Contact.getRecordTypeInfosByDeveloperName().get('Ref').getRecordTypeId();
    public static Id contactURMRT = Schema.SObjectType.Contact.getRecordTypeInfosByDeveloperName().get('URM').getRecordTypeId();
    
   
    public static Boolean shareHHAcctsWithAgencyStaff(List<AccountContactRelation> newRelation, Map<Id, AccountContactRelation> newMap){
        Map<Id, List<AccountContactRelation>> empWithHHRelation = new Map<Id, List<AccountContactRelation>>();
        Map<Id, Contact> triggeringContact = new Map<Id, Contact>();
        Map<Id, List<Id>> agencyIDWithEmpIDs = new Map<Id, List<Id>>();
        List<Contact> provider = new List<Contact>(); 
        List<Account> agency = new List<Account>();
        List<Contact> agencyProviders = new List<Contact>();
        List<AccountContactRelation> HHRelations = null;
        List<AccountContactRelation> householdRelationshipsToCreate = new List<AccountContactRelation>();
        Set<Id> hhSet = new Set<Id>();
        Set<Id> agencyIds = new Set<Id>();
        
        for(AccountContactRelation acr : newRelation){
            
            HHRelations = empWithHHRelation.get(acr.contactId);
            if(HHRelations == null){
                HHRelations = new List<AccountContactRelation>();
            }
            HHRelations.add(acr);
            empWithHHRelation.put(acr.ContactId, HHRelations); 
        }
        //only share out relations that are from agency contacts 
        if(empWithHHRelation.size()>0){
            
            provider = [Select Id, AccountId, RecordTypeId from Contact where Id IN:empWithHHRelation.keyset()
                        AND RecordTypeID =:contactProviderRT
                        AND Account.RecordTypeId =:accountOrganizationRT];
            
            for(Contact c : provider){
                triggeringContact.put(c.id, c);
                agencyIds.add(c.AccountId);
            }
            
            if(triggeringContact.size()>0){
                
                agencyProviders = [SELECT Id, AccountId, Name, RecordTypeId FROM Contact WHERE RecordTypeId =:contactProviderRT
                                   AND AccountId IN:agencyIds
                                   AND Account.RecordTypeId =:accountOrganizationRT];
                
                List<Id> providerIds = null;
                
                for(Contact ap : agencyProviders){
                    
                    providerIds = agencyIDWithEmpIDs.get(ap.AccountId);
                    
                    if(providerIds == null){
                        providerIds = new List<Id>();
                    }
                    providerIds.add(ap.id);
                    agencyIDWithEmpIDs.put(ap.AccountId, providerIds);
                }
                
                for(Contact tc : triggeringContact.values()){
                    
                    for(Id pId : agencyIdWithEmpIds.get(tc.accountId)){
                        //Household Accounts that need to be shared 
                        for(AccountContactRelation HH : empWithHHRelation.get(tc.id)){
                            
                            AccountContactRelation acr = new AccountContactRelation();
                            acr.ContactId = pId;
                            acr.AccountId = HH.AccountId;
                            acr.IsActive = true;
                            acr.Roles = 'Agency';
                            householdRelationshipsToCreate.add(acr);
                        }
                    }
                }
                
                if(householdRelationshipsToCreate.size()>0){
                    
                    Map<String, String> relationErrMap = new Map<String, String>();
                    Set<String> successSet = new Set<String>();
                    
                    Database.SaveResult[] srList = Database.insert(householdRelationshipsToCreate, false);
                    for (Integer index = 0, size = srList.size(); index < size; index++) {
                        if (srList[index].isSuccess()) {
                            // Operation was successful, so get the ID of the record that was processed
                            successSet.add(srList[index].id);
                            System.debug(srList[index] +' AcctContRelation was successfully created.');
                        }
                        else {
                            // Operation failed, so get all errors                
                            for(Database.Error err : srList[index].getErrors()) {
                                System.debug('### ErrorCode = ' +err.StatusCode ); //shows error
                                System.debug('### On Field = ' + err.getFields()); //shows field with error
                                System.debug('### on Source Id = ' + householdRelationshipsToCreate[index].Id); //shows trip form ID
                                String errMsg = err.getMessage() + '\n' + err.getStatusCode() + '\n' + err.getFields();
                                // Add to Error map
                                relationErrMap.put(householdRelationshipsToCreate[index].Id, errMsg);
                            }
                        }
                        
                    }
                }
            }
            
        }
        return true;
    }
}