We have this nightly code that runs in our org. Recently, we started receiving APEX CPU Time limit exceeded errors and we're looking for ways top optimize the code. Anyone have any suggestions on how to optimize?

Some Notes:

  1. Our Contact object has about 5 validation rules and about a dozen workflow rules, so that slow things down a bit - and it shows that in the detailed debug logs.

  2. The second for loop is necessary to trigger a flag TriggerNextDateEmail, that then triggers a time-based workflow rule. Based on the date that is being updated, that time based workflow sends an email to the Contact. You can't combine that second for loop into the first because then the flag doesn't get shut off and then turned back on. Without that off-on, the time based workflow doesn't get re-triggered (this is a recurring email that goes out every several days or so)

  3. So far this is failing on an update that is working though about 850 contacts

global class cls_update_Next_Date implements Schedulable { global void execute(SchedulableContext ctx) { list conlist = new list(); list updateconlist = new list(); conlist.clear(); updateconlist.clear(); conlist = [select id,Status__c,NextDate__c,Next_Date_Calculation__c from Contact where NextDate__c <=:date.Today() and Status__c = 'Enrolled'];

for(contact con:conlist) { con.NextDate__c = con.Next_Date_Calculation__c; con.TriggerNextDateEmail__c = false; //need to reset this so email under time based workflow trigger is sent updateconlist.add(con); } if(updateconlist.size()>0) { update updateconlist; } // TriggerNextDateEmail__c has been set to false, now need to reset it to true so the email is re-triggered updateconlist.clear(); for(contact con:conlist) { con.TriggerNextDateEmail__c = true; updateconlist.add(con); } if(updateconlist.size()>0) { update updateconlist; } } }

Move the processing into a batch implementation. Then the scheduled class with just start the batch processing.

The batch will breakup the SOQL results. Then you can use the scope parameter for Database.executeBatch to limit the number of Contacts per batch execution.

Note that each batch execution gets separate governor limits.

  • Thanks, this is very helpful. Been reading up on this and have a question for the headers/prototypes: Should I use two classes here, or one. I'm thinking two. First would be global class cls_update_Next_Date implements Schedulable and a second, let's call it, global class cls_NextDateBatchJob. Then in cls_update_Next_Date I would instanstiate cls_NextDateBatchJob via a Database.executeBatch(new cls_NextDateBatchJob() where of course I'm missing some args. Assuming that's right, worth noting that the test class is going to be a pain to write here! Jul 21 '15 at 18:36
  • Personally I like to keep the Schedulable and Batchable classes separate, although I've heard of people combining them. The scheduled class can be minimal to keep the test simple. You may also want to check the current number of running batch jobs. Jul 22 '15 at 2:07
  • On your last point re batch jobs. I had a question about how the queue works and didn't understand the documentation where it says "Up to 5 batch jobs can be queued or active concurrently": If I create this batch job and set scope at 200 and have 1100 records to process, what happens re the queue. The queue (assuming not flex) only queues 5 jobs, so what happens to the last 100 records? are those queued too? Jul 22 '15 at 18:29
  • @StartingAgain The 5 active/queued batch jobs (or 100 in holding with flex queue) only applies to separate batches. Each call to execute within the batch doesn't contribute to that limit. You could process many thousands of records with a scope of 1 with no problem. Although the smaller scope would probably increase the end to end execution time. Jul 22 '15 at 19:23

I suggest to check your process time on queries and loops. Check the same in trigger as well.

add a statement before your query(s) and after it and also before the loop(s) and after them to see what is eating up most of the time. Following is the example:

Long queryStartTime = DateTime.now.getTime();

--your query statements and logic here

Long queryEndTime = DateTime.now.getTime();

--now check the time it is taking

System.debug(LoggingLevel.ERROR, 'Query time taken: ' + (queryEndTime - queryStartTime ));

You may find your problem with this. If not write back.

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.