1

I have been reading about CPU time limit, but can't seem to find a solution to my scheduler. Can someone help me? How can I improve this? It works in Sandbox, but not in Production.

Error message:

Scheduler: failed to execute scheduled job: jobId: 7071L00007omAoo, class: common.apex.async.AsyncApexJobObject, reason: Apex CPU time limit exceeded

Apex Class:

global class UpdateAssetsScheduler implements Schedulable {    

   global void execute(SchedulableContext SC) {
       List<Order> orders = [Select Id,Name,AccountId
                             from Order
                             where Cancel_All_Accounts__c = true
                             and (Type = 'A Cancellation' OR Type= 'B Cancellation')
                             and Approved_Cancel_All_Assets__c = true
                             and Billing_Start_Date__c =: System.Today()
                            ];

       List<String> accountIds = new List<String>();
       for(Order o: orders){
           if(o.AccountId != null){
               accountIds.add(o.AccountId);
           }
       }
       List<Account> accounts = [Select Id,Name,Account_Status__c,(select Id,Name,Status from Assets)
                                 from Account
                                where Id =: accountIds
                                ];
       List<Asset> assets = new List<Asset>();

       for(Account acc : accounts){
           List<Asset> child_assets = acc.Assets;
           for(Asset a : child_assets){
               a.Status = 'Not Active';
               assets.add(a);
           }
           acc.Account_Status__c = 'Non-Active';
       }

       update assets;
       update accounts;
   }
}
5
  • 4
    What volume of data is returned by the queries? Most likely you'll have to implement a batch class.
    – David Reed
    Feb 24, 2020 at 14:43
  • The first clue of code likely to suffer from high CPU usage is the use of nested for loops. These are avoided in various different ways based on use case; here I'd agree with David Reed that you likely need to run a batch across Accounts.
    – Phil W
    Feb 24, 2020 at 15:02
  • @DavidReed & PhilW I'll have to do some reading then. I haven't done a batch class yet.
    – Sharky
    Feb 24, 2020 at 15:06
  • 1
    @PhilW The nested for loop is an O(n) complexity, not O(n^2) complexity.
    – sfdcfox
    Feb 24, 2020 at 17:09
  • That is true for this case, though avoiding nested loops is frequently a good thing since they are expensive operations in CPU terms within Apex. Clearly it isn't always possible to efficiently organize the code to remove them, but the more you can push behind Apex API methods the better.
    – Phil W
    Feb 24, 2020 at 17:32

1 Answer 1

4

You'll eventually want to move to batchable, but your code here can be optimized. I have no idea if the changes will be substantial enough to get under the CPU limits, but it's worth a try:

Asset[] assets = [
    SELECT Id 
    FROM Assets 
    WHERE AccountId IN (
        SELECT AccountId 
        FROM Order 
        WHERE Cancel_All_Accounts__c = TRUE AND 
            Type IN ('A Cancellation','B Cancellation') AND 
            Billing_Start_Date__c = TODAY AND
            AccountId != NULL
    ) AND Status != 'Not Active'];
Account[] accounts = [
    SELECT Id 
    FROM Account
    WHERE Id IN (SELECT AccountId FROM Asset WHERE Id = :assets) AND
        Account_Status__c != 'Non-Active'
];
for(Asset record: assets) {
    record.Status = 'Not Active';
}
for(Account record: accounts) {
    record.Account_Status__c = 'Non-Active';
}
update assets;
update accounts;

Here, we use the database to extract the AccountId values, and place both the assets and accounts in their own lists, since the data is not dependent, we don't need to do any extra processing.

The main problem is likely with your triggers taking too much CPU time, however. Optimization of your other code is imperative for bulk updates and day-to-day operations.

You must log in to answer this question.

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