We are using below Trigger framework
public virtual class TriggerHandler {
// static map of handlername, times run() was invoked
private static Map<String, LoopCount> loopCountMap;
private static Set<String> bypassedHandlers;
static {
loopCountMap = new Map<String, LoopCount>();
bypassedHandlers = new Set<String>();
}
public TriggerHandler() {
}
//method that will be called during execution
public void run() {
if(!validateRun()) return;
addToLoopCount();
if(Trigger.isBefore && Trigger.isInsert) {
this.beforeInsert();
} else if(Trigger.isBefore && Trigger.isUpdate) {
this.beforeUpdate();
} else if(Trigger.isBefore && Trigger.isDelete) {
this.beforeDelete();
} else if(Trigger.isAfter && Trigger.isInsert) {
this.afterInsert();
} else if(Trigger.isAfter && Trigger.isUpdate) {
this.afterUpdate();
} else if(Trigger.isAfter && Trigger.isDelete) {
this.afterDelete();
} else if(Trigger.isAfter && Trigger.isUndelete) {
this.afterUndelete();
}
}
public void setMaxLoopCount(Integer max) {
String handlerName = getHandlerName();
if(!TriggerHandler.loopCountMap.containsKey(handlerName)) {
TriggerHandler.loopCountMap.put(handlerName, new LoopCount(max));
} else {
TriggerHandler.loopCountMap.get(handlerName).setMax(max);
}
}
public void clearMaxLoopCount() {
this.setMaxLoopCount(-1);
}
public static void bypass(String handlerName) {
TriggerHandler.bypassedHandlers.add(handlerName);
}
public static void clearBypass(String handlerName) {
TriggerHandler.bypassedHandlers.remove(handlerName);
}
public static Boolean isBypassed(String handlerName) {
return TriggerHandler.bypassedHandlers.contains(handlerName);
}
public static void clearAllBypasses() {
TriggerHandler.bypassedHandlers.clear();
}
//allows trigger methods to be deactivated from Custom Setting configuration
public static Boolean methodShouldRun(String methodName){
return Trigger_Method_Control__c.getValues(methodName) == null ? true : !Trigger_Method_Control__c.getValues(methodName).Deactivated__c;
}
private void addToLoopCount() {
String handlerName = getHandlerName();
if(TriggerHandler.loopCountMap.containsKey(handlerName)) {
Boolean exceeded = TriggerHandler.loopCountMap.get(handlerName).increment();
if(exceeded) {
Integer max = TriggerHandler.loopCountMap.get(handlerName).max;
throw new TriggerHandlerException('Maximum loop count of ' + String.valueOf(max) + ' reached in ' + handlerName);
}
}
}
// make sure this trigger should continue to run
private Boolean validateRun() {
if(!Trigger.isExecuting) {
throw new TriggerHandlerException('Trigger handler called outside of Trigger execution');
}
if(TriggerHandler.bypassedHandlers.contains(getHandlerName())) {
return false;
}
return true;
}
private String getHandlerName() {
return String.valueOf(this).substring(0,String.valueOf(this).indexOf(':'));
}
// context-specific methods for override
protected virtual void beforeInsert(){}
protected virtual void beforeUpdate(){}
protected virtual void beforeDelete(){}
protected virtual void afterInsert(){}
protected virtual void afterUpdate(){}
protected virtual void afterDelete(){}
protected virtual void afterUndelete(){}
// inner class for managing the loop count per handler
private class LoopCount {
private Integer max;
private Integer count;
public LoopCount() {
this.max = 5;
this.count = 0;
}
public LoopCount(Integer max) {
this.max = max;
this.count = 0;
}
public Boolean increment() {
this.count++;
return this.exceeded();
}
public Boolean exceeded() {
if(this.max < 0) return false;
if(this.count > this.max) {
return true;
}
return false;
}
public Integer getMax() {
return this.max;
}
public Integer getCount() {
return this.count;
}
public void setMax(Integer max) {
this.max = max;
}
}
// exception class
public class TriggerHandlerException extends Exception {}
}
and this is our trigger handler class:
public class CaseTriggerHandler extends TriggerHandler {
//private members represent the trigger data passed in constructor
private List<Case> oldCases;
private List<Case> newCases;
private Map<Id,Case> oldCaseMap;
private Map<Id,Case> newCaseMap;
public static Integer runCount = 0;
//constructor populates private members and handles typecasting for ease of later use
public CaseTriggerHandler(){
//this.setMaxLoopCount(1);
this.oldCases = (List<Case>)Trigger.old;
this.newCases = (List<Case>)Trigger.new;
this.oldCaseMap = (Map<Id,Case>)Trigger.oldMap;
this.newCaseMap = (Map<Id,Case>)Trigger.newMap;
}
public override void beforeInsert() {
System.debug('case before insert');
CaseUtils.setRecordType(newCases);
CaseUtils.LeadForm(newCases,null);
}
public override void afterInsert() {
System.debug('case after insert');
CaseUtils.addOwnerAsTeamMember(null, newCaseMap);
CustomerUtils.checkChildCaseForSLADateFix(newCases);
CustomerUtils.manageREOmniMarkers(newCaseMap);
System.debug('after case insert');
}
public override void beforeUpdate() {
System.debug('case before update');
runCount++;
if(runCount <= 3){
CaseUtils.LeadForm(newCases,oldCaseMap);
CustomerUtils.SLAmanagement(oldCases, newCaseMap);
TaskUtils.taskManagement(oldCases, newCaseMap);
}
}
public override void afterUpdate() {
System.debug('case after update');
if(runCount <= 3){
CaseUtils.addOwnerAsTeamMember(oldCaseMap, newCaseMap);
CaseUtils.updateMOCase(oldCaseMap, newCaseMap);
FarmUtils.ForSalesUpdate(oldCases,newCaseMap);
OtherCustomerUtils.ForCustomerSalesUpdate(oldCases,newCaseMap);
FarmChatterUtils.autoFollowRecord(oldCases, newCaseMap);
}
}
public override void beforeDelete() {}
public override void afterDelete() {
CustomerUtils.deleteAssignedCaseMembers(oldCaseMap.keySet());
}
public override void afterUndelete() {}
}
We are hitting CPU time limit when trying to update a list of 8 records from which one being parent record and the rest are child records. We also have a bunch of Processes, Flows and Workflows on Case object. The error thrown wasn't much helpful because a simple field update was shown as a culprit though it is not the actual root cause but when i checked the logs, i could see that below methods are being called multiple times which is consuming more time all together.
Is there a better way to avoid this and improvise this pattern to be more efficient?
Thanks in advance!