Batch Apex: [REQUEST_RUNNING_TOO_LONG] - can I optimise my class?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
2
down vote

favorite












I've written a batch class that grabs order ID's using a selective query in a QueryLocator method. I then execute my batch by running a SOQL for loop to get and update relevant big object records that have a matching Id/value on one of the orders retrieved.



Is there a better (read: more efficient) way that I can retrieve these big object records to update? Should I be using a map instead?



Note that one of my index fields on the big object isn't queryable without first finding that big object record (as in, its not available on any other sObjects).



My start/execute methods are below. Any suggestions on increasing efficiency to get rid of this error would be appreciated.



global class UpdateArchivedOrderLinesBatch implements Database.Batchable<sObject> {

global Database.QueryLocator start(Database.BatchableContext bc)
return Database.getQueryLocator([
SELECT Id FROM Sales_Order__c WHERE Actual_Order_Date__c < 2014-01-01T01:02:03z
]);


global void execute(Database.BatchableContext bc, List<Sales_Order__c> orderIds)

List<Order_Line__b> olToUpdate = new List<Order_Line__b>();

Decimal cad2usd = 0.77018;
Decimal chf2usd = 1.00812;
Decimal dkk2usd = 0.154710;
Decimal eur2usd = 1.154220;
Decimal gbp2usd = 1.3233;
Decimal sek2usd = 0.1119;

for(Order_Line__b ol : [
SELECT Order__c, Order_Line_ExtId__c, Account__c, Account_Country__c, Actual_Order_Date__c, Currency__c,
Final_Price__c, Final_Price_EUR__c, Net_Price__c, Returned__c, Item_Revenue__c, Return_Amount__c, Return_Amount_USD__c, Return_Amount_EUR__c,
Order__r.Copy_Actual_Order_Date__c, Order__r.Copy_Account__c, Order__r.Account_Country__c, Order__r.Currency__c
FROM Order_Line__b
WHERE Order__c IN :orderIds])

Order_Line__b line = new Order_Line__b();

// set indexes to ensure this only updates existing records
line.Order__c = ol.Order__c;
line.Order_Line_ExtId__c = ol.Order_Line_ExtId__c;

// backfill new field values from the related Order
line.Actual_Order_Date__c = ol.Order__r.Copy_Actual_Order_Date__c;
line.Account__c = ol.Order__r.Copy_Account__c;
line.Account_Country__c = ol.Order__r.Account_Country__c;
if(ol.Return_Amount__c == null && ol.Returned__c == 'true' && ol.Final_Price__c != null)
line.Return_Amount__c = ol.Final_Price__c;


// add missing EUR return amounts to lines
if(ol.Return_Amount_EUR__c == null && ol.Returned__c == 'true' && ol.Final_Price_EUR__c != null)
line.Return_Amount_EUR__c = ol.Final_Price_EUR__c;


// add USD values to CAD lines
if(ol.Order__r.Currency__c == 'CAD' && ol.Net_Price__c != null && ol.Final_Price__c != null)
line.Final_Price_USD__c = ol.Final_Price__c * cad2usd;
line.Net_Price_USD__c = ol.Net_Price__c * cad2usd;
if(ol.Return_Amount_USD__c == null && ol.Returned__c == 'true')
line.Return_Amount_USD__c = ol.Final_Price__c * cad2usd;


// add USD values to USD lines
if(ol.Order__r.Currency__c == 'USD' && ol.Net_Price__c != null && ol.Final_Price__c != null)
line.Final_Price_USD__c = ol.Final_Price__c;
line.Net_Price_USD__c = ol.Net_Price__c;
if(ol.Return_Amount_USD__c == null && ol.Returned__c == 'true')
line.Return_Amount_USD__c = ol.Final_Price__c;



olToUpdate.add(line);


Database.insertImmediate(olToUpdate);










share|improve this question





















  • What fields have you indexed in the big objects?
    – Pranay Jaiswal
    4 hours ago










  • I've indexed Order__c (the lookup to the order) and an external Id (Order_Line_ExtId__c) that was on the OrderLine back when it was a custom object (before I archived it into a big object....therefore that custom object record no longer exists)
    – Mark S
    4 hours ago











  • @PranayJaiswal now that I think about it, the external Id field on the big object does start with a field thats queryable on the Sales_Order__c record (order number). Order_Line__b.Order_Line_ExtId__c value will always start with the Sales_Order__c.Order_Number__c value... So if an order number is 1234, the external Id value on the big object would be 1234abcd. Maybe I could better use a map to first loop over the order list and store Id/OrderNumber, then iterate over big objects to find the matches?
    – Mark S
    4 hours ago

















up vote
2
down vote

favorite












I've written a batch class that grabs order ID's using a selective query in a QueryLocator method. I then execute my batch by running a SOQL for loop to get and update relevant big object records that have a matching Id/value on one of the orders retrieved.



Is there a better (read: more efficient) way that I can retrieve these big object records to update? Should I be using a map instead?



Note that one of my index fields on the big object isn't queryable without first finding that big object record (as in, its not available on any other sObjects).



My start/execute methods are below. Any suggestions on increasing efficiency to get rid of this error would be appreciated.



global class UpdateArchivedOrderLinesBatch implements Database.Batchable<sObject> {

global Database.QueryLocator start(Database.BatchableContext bc)
return Database.getQueryLocator([
SELECT Id FROM Sales_Order__c WHERE Actual_Order_Date__c < 2014-01-01T01:02:03z
]);


global void execute(Database.BatchableContext bc, List<Sales_Order__c> orderIds)

List<Order_Line__b> olToUpdate = new List<Order_Line__b>();

Decimal cad2usd = 0.77018;
Decimal chf2usd = 1.00812;
Decimal dkk2usd = 0.154710;
Decimal eur2usd = 1.154220;
Decimal gbp2usd = 1.3233;
Decimal sek2usd = 0.1119;

for(Order_Line__b ol : [
SELECT Order__c, Order_Line_ExtId__c, Account__c, Account_Country__c, Actual_Order_Date__c, Currency__c,
Final_Price__c, Final_Price_EUR__c, Net_Price__c, Returned__c, Item_Revenue__c, Return_Amount__c, Return_Amount_USD__c, Return_Amount_EUR__c,
Order__r.Copy_Actual_Order_Date__c, Order__r.Copy_Account__c, Order__r.Account_Country__c, Order__r.Currency__c
FROM Order_Line__b
WHERE Order__c IN :orderIds])

Order_Line__b line = new Order_Line__b();

// set indexes to ensure this only updates existing records
line.Order__c = ol.Order__c;
line.Order_Line_ExtId__c = ol.Order_Line_ExtId__c;

// backfill new field values from the related Order
line.Actual_Order_Date__c = ol.Order__r.Copy_Actual_Order_Date__c;
line.Account__c = ol.Order__r.Copy_Account__c;
line.Account_Country__c = ol.Order__r.Account_Country__c;
if(ol.Return_Amount__c == null && ol.Returned__c == 'true' && ol.Final_Price__c != null)
line.Return_Amount__c = ol.Final_Price__c;


// add missing EUR return amounts to lines
if(ol.Return_Amount_EUR__c == null && ol.Returned__c == 'true' && ol.Final_Price_EUR__c != null)
line.Return_Amount_EUR__c = ol.Final_Price_EUR__c;


// add USD values to CAD lines
if(ol.Order__r.Currency__c == 'CAD' && ol.Net_Price__c != null && ol.Final_Price__c != null)
line.Final_Price_USD__c = ol.Final_Price__c * cad2usd;
line.Net_Price_USD__c = ol.Net_Price__c * cad2usd;
if(ol.Return_Amount_USD__c == null && ol.Returned__c == 'true')
line.Return_Amount_USD__c = ol.Final_Price__c * cad2usd;


// add USD values to USD lines
if(ol.Order__r.Currency__c == 'USD' && ol.Net_Price__c != null && ol.Final_Price__c != null)
line.Final_Price_USD__c = ol.Final_Price__c;
line.Net_Price_USD__c = ol.Net_Price__c;
if(ol.Return_Amount_USD__c == null && ol.Returned__c == 'true')
line.Return_Amount_USD__c = ol.Final_Price__c;



olToUpdate.add(line);


Database.insertImmediate(olToUpdate);










share|improve this question





















  • What fields have you indexed in the big objects?
    – Pranay Jaiswal
    4 hours ago










  • I've indexed Order__c (the lookup to the order) and an external Id (Order_Line_ExtId__c) that was on the OrderLine back when it was a custom object (before I archived it into a big object....therefore that custom object record no longer exists)
    – Mark S
    4 hours ago











  • @PranayJaiswal now that I think about it, the external Id field on the big object does start with a field thats queryable on the Sales_Order__c record (order number). Order_Line__b.Order_Line_ExtId__c value will always start with the Sales_Order__c.Order_Number__c value... So if an order number is 1234, the external Id value on the big object would be 1234abcd. Maybe I could better use a map to first loop over the order list and store Id/OrderNumber, then iterate over big objects to find the matches?
    – Mark S
    4 hours ago













up vote
2
down vote

favorite









up vote
2
down vote

favorite











I've written a batch class that grabs order ID's using a selective query in a QueryLocator method. I then execute my batch by running a SOQL for loop to get and update relevant big object records that have a matching Id/value on one of the orders retrieved.



Is there a better (read: more efficient) way that I can retrieve these big object records to update? Should I be using a map instead?



Note that one of my index fields on the big object isn't queryable without first finding that big object record (as in, its not available on any other sObjects).



My start/execute methods are below. Any suggestions on increasing efficiency to get rid of this error would be appreciated.



global class UpdateArchivedOrderLinesBatch implements Database.Batchable<sObject> {

global Database.QueryLocator start(Database.BatchableContext bc)
return Database.getQueryLocator([
SELECT Id FROM Sales_Order__c WHERE Actual_Order_Date__c < 2014-01-01T01:02:03z
]);


global void execute(Database.BatchableContext bc, List<Sales_Order__c> orderIds)

List<Order_Line__b> olToUpdate = new List<Order_Line__b>();

Decimal cad2usd = 0.77018;
Decimal chf2usd = 1.00812;
Decimal dkk2usd = 0.154710;
Decimal eur2usd = 1.154220;
Decimal gbp2usd = 1.3233;
Decimal sek2usd = 0.1119;

for(Order_Line__b ol : [
SELECT Order__c, Order_Line_ExtId__c, Account__c, Account_Country__c, Actual_Order_Date__c, Currency__c,
Final_Price__c, Final_Price_EUR__c, Net_Price__c, Returned__c, Item_Revenue__c, Return_Amount__c, Return_Amount_USD__c, Return_Amount_EUR__c,
Order__r.Copy_Actual_Order_Date__c, Order__r.Copy_Account__c, Order__r.Account_Country__c, Order__r.Currency__c
FROM Order_Line__b
WHERE Order__c IN :orderIds])

Order_Line__b line = new Order_Line__b();

// set indexes to ensure this only updates existing records
line.Order__c = ol.Order__c;
line.Order_Line_ExtId__c = ol.Order_Line_ExtId__c;

// backfill new field values from the related Order
line.Actual_Order_Date__c = ol.Order__r.Copy_Actual_Order_Date__c;
line.Account__c = ol.Order__r.Copy_Account__c;
line.Account_Country__c = ol.Order__r.Account_Country__c;
if(ol.Return_Amount__c == null && ol.Returned__c == 'true' && ol.Final_Price__c != null)
line.Return_Amount__c = ol.Final_Price__c;


// add missing EUR return amounts to lines
if(ol.Return_Amount_EUR__c == null && ol.Returned__c == 'true' && ol.Final_Price_EUR__c != null)
line.Return_Amount_EUR__c = ol.Final_Price_EUR__c;


// add USD values to CAD lines
if(ol.Order__r.Currency__c == 'CAD' && ol.Net_Price__c != null && ol.Final_Price__c != null)
line.Final_Price_USD__c = ol.Final_Price__c * cad2usd;
line.Net_Price_USD__c = ol.Net_Price__c * cad2usd;
if(ol.Return_Amount_USD__c == null && ol.Returned__c == 'true')
line.Return_Amount_USD__c = ol.Final_Price__c * cad2usd;


// add USD values to USD lines
if(ol.Order__r.Currency__c == 'USD' && ol.Net_Price__c != null && ol.Final_Price__c != null)
line.Final_Price_USD__c = ol.Final_Price__c;
line.Net_Price_USD__c = ol.Net_Price__c;
if(ol.Return_Amount_USD__c == null && ol.Returned__c == 'true')
line.Return_Amount_USD__c = ol.Final_Price__c;



olToUpdate.add(line);


Database.insertImmediate(olToUpdate);










share|improve this question













I've written a batch class that grabs order ID's using a selective query in a QueryLocator method. I then execute my batch by running a SOQL for loop to get and update relevant big object records that have a matching Id/value on one of the orders retrieved.



Is there a better (read: more efficient) way that I can retrieve these big object records to update? Should I be using a map instead?



Note that one of my index fields on the big object isn't queryable without first finding that big object record (as in, its not available on any other sObjects).



My start/execute methods are below. Any suggestions on increasing efficiency to get rid of this error would be appreciated.



global class UpdateArchivedOrderLinesBatch implements Database.Batchable<sObject> {

global Database.QueryLocator start(Database.BatchableContext bc)
return Database.getQueryLocator([
SELECT Id FROM Sales_Order__c WHERE Actual_Order_Date__c < 2014-01-01T01:02:03z
]);


global void execute(Database.BatchableContext bc, List<Sales_Order__c> orderIds)

List<Order_Line__b> olToUpdate = new List<Order_Line__b>();

Decimal cad2usd = 0.77018;
Decimal chf2usd = 1.00812;
Decimal dkk2usd = 0.154710;
Decimal eur2usd = 1.154220;
Decimal gbp2usd = 1.3233;
Decimal sek2usd = 0.1119;

for(Order_Line__b ol : [
SELECT Order__c, Order_Line_ExtId__c, Account__c, Account_Country__c, Actual_Order_Date__c, Currency__c,
Final_Price__c, Final_Price_EUR__c, Net_Price__c, Returned__c, Item_Revenue__c, Return_Amount__c, Return_Amount_USD__c, Return_Amount_EUR__c,
Order__r.Copy_Actual_Order_Date__c, Order__r.Copy_Account__c, Order__r.Account_Country__c, Order__r.Currency__c
FROM Order_Line__b
WHERE Order__c IN :orderIds])

Order_Line__b line = new Order_Line__b();

// set indexes to ensure this only updates existing records
line.Order__c = ol.Order__c;
line.Order_Line_ExtId__c = ol.Order_Line_ExtId__c;

// backfill new field values from the related Order
line.Actual_Order_Date__c = ol.Order__r.Copy_Actual_Order_Date__c;
line.Account__c = ol.Order__r.Copy_Account__c;
line.Account_Country__c = ol.Order__r.Account_Country__c;
if(ol.Return_Amount__c == null && ol.Returned__c == 'true' && ol.Final_Price__c != null)
line.Return_Amount__c = ol.Final_Price__c;


// add missing EUR return amounts to lines
if(ol.Return_Amount_EUR__c == null && ol.Returned__c == 'true' && ol.Final_Price_EUR__c != null)
line.Return_Amount_EUR__c = ol.Final_Price_EUR__c;


// add USD values to CAD lines
if(ol.Order__r.Currency__c == 'CAD' && ol.Net_Price__c != null && ol.Final_Price__c != null)
line.Final_Price_USD__c = ol.Final_Price__c * cad2usd;
line.Net_Price_USD__c = ol.Net_Price__c * cad2usd;
if(ol.Return_Amount_USD__c == null && ol.Returned__c == 'true')
line.Return_Amount_USD__c = ol.Final_Price__c * cad2usd;


// add USD values to USD lines
if(ol.Order__r.Currency__c == 'USD' && ol.Net_Price__c != null && ol.Final_Price__c != null)
line.Final_Price_USD__c = ol.Final_Price__c;
line.Net_Price_USD__c = ol.Net_Price__c;
if(ol.Return_Amount_USD__c == null && ol.Returned__c == 'true')
line.Return_Amount_USD__c = ol.Final_Price__c;



olToUpdate.add(line);


Database.insertImmediate(olToUpdate);







apex class






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked 4 hours ago









Mark S

465




465











  • What fields have you indexed in the big objects?
    – Pranay Jaiswal
    4 hours ago










  • I've indexed Order__c (the lookup to the order) and an external Id (Order_Line_ExtId__c) that was on the OrderLine back when it was a custom object (before I archived it into a big object....therefore that custom object record no longer exists)
    – Mark S
    4 hours ago











  • @PranayJaiswal now that I think about it, the external Id field on the big object does start with a field thats queryable on the Sales_Order__c record (order number). Order_Line__b.Order_Line_ExtId__c value will always start with the Sales_Order__c.Order_Number__c value... So if an order number is 1234, the external Id value on the big object would be 1234abcd. Maybe I could better use a map to first loop over the order list and store Id/OrderNumber, then iterate over big objects to find the matches?
    – Mark S
    4 hours ago

















  • What fields have you indexed in the big objects?
    – Pranay Jaiswal
    4 hours ago










  • I've indexed Order__c (the lookup to the order) and an external Id (Order_Line_ExtId__c) that was on the OrderLine back when it was a custom object (before I archived it into a big object....therefore that custom object record no longer exists)
    – Mark S
    4 hours ago











  • @PranayJaiswal now that I think about it, the external Id field on the big object does start with a field thats queryable on the Sales_Order__c record (order number). Order_Line__b.Order_Line_ExtId__c value will always start with the Sales_Order__c.Order_Number__c value... So if an order number is 1234, the external Id value on the big object would be 1234abcd. Maybe I could better use a map to first loop over the order list and store Id/OrderNumber, then iterate over big objects to find the matches?
    – Mark S
    4 hours ago
















What fields have you indexed in the big objects?
– Pranay Jaiswal
4 hours ago




What fields have you indexed in the big objects?
– Pranay Jaiswal
4 hours ago












I've indexed Order__c (the lookup to the order) and an external Id (Order_Line_ExtId__c) that was on the OrderLine back when it was a custom object (before I archived it into a big object....therefore that custom object record no longer exists)
– Mark S
4 hours ago





I've indexed Order__c (the lookup to the order) and an external Id (Order_Line_ExtId__c) that was on the OrderLine back when it was a custom object (before I archived it into a big object....therefore that custom object record no longer exists)
– Mark S
4 hours ago













@PranayJaiswal now that I think about it, the external Id field on the big object does start with a field thats queryable on the Sales_Order__c record (order number). Order_Line__b.Order_Line_ExtId__c value will always start with the Sales_Order__c.Order_Number__c value... So if an order number is 1234, the external Id value on the big object would be 1234abcd. Maybe I could better use a map to first loop over the order list and store Id/OrderNumber, then iterate over big objects to find the matches?
– Mark S
4 hours ago





@PranayJaiswal now that I think about it, the external Id field on the big object does start with a field thats queryable on the Sales_Order__c record (order number). Order_Line__b.Order_Line_ExtId__c value will always start with the Sales_Order__c.Order_Number__c value... So if an order number is 1234, the external Id value on the big object would be 1234abcd. Maybe I could better use a map to first loop over the order list and store Id/OrderNumber, then iterate over big objects to find the matches?
– Mark S
4 hours ago











1 Answer
1






active

oldest

votes

















up vote
3
down vote













Although my query in the Start method was using an index, it wasn't qualifying as Selective in the dev console's Query Plan tool. The cost returned was 1.3, where SF says anything above 1 is no longer considered selective (see: https://help.salesforce.com/articleView?id=000199003&type=1)



I've added the following to my start method/query:



 String query = 'SELECT Id FROM Sales_Order__c ' + Label.UpdateArchivedOrderLinesWhere;

global Database.QueryLocator start(Database.BatchableContext bc)
return Database.getQueryLocator(this.query);



...and I'm now using a custom label to adjust my WHERE clause directly from production using timeframes on an indexed field (e.g. WHERE Actual_Order_Date__c > 2014-01-01T01:02:02z AND Actual_Order_Date__c < 2014-06-01T01:02:02z). I'll do this in chunks using as big of a timeframe as I can, while still maintaining a selective query.



If you have other optimisation suggestions please feel free to post them, but this is now running successfully in prod against ±7mill big objects rows related to ±2mill various custom object rows.






share|improve this answer






















    Your Answer







    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "459"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsalesforce.stackexchange.com%2fquestions%2f237262%2fbatch-apex-request-running-too-long-can-i-optimise-my-class%23new-answer', 'question_page');

    );

    Post as a guest






























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    3
    down vote













    Although my query in the Start method was using an index, it wasn't qualifying as Selective in the dev console's Query Plan tool. The cost returned was 1.3, where SF says anything above 1 is no longer considered selective (see: https://help.salesforce.com/articleView?id=000199003&type=1)



    I've added the following to my start method/query:



     String query = 'SELECT Id FROM Sales_Order__c ' + Label.UpdateArchivedOrderLinesWhere;

    global Database.QueryLocator start(Database.BatchableContext bc)
    return Database.getQueryLocator(this.query);



    ...and I'm now using a custom label to adjust my WHERE clause directly from production using timeframes on an indexed field (e.g. WHERE Actual_Order_Date__c > 2014-01-01T01:02:02z AND Actual_Order_Date__c < 2014-06-01T01:02:02z). I'll do this in chunks using as big of a timeframe as I can, while still maintaining a selective query.



    If you have other optimisation suggestions please feel free to post them, but this is now running successfully in prod against ±7mill big objects rows related to ±2mill various custom object rows.






    share|improve this answer


























      up vote
      3
      down vote













      Although my query in the Start method was using an index, it wasn't qualifying as Selective in the dev console's Query Plan tool. The cost returned was 1.3, where SF says anything above 1 is no longer considered selective (see: https://help.salesforce.com/articleView?id=000199003&type=1)



      I've added the following to my start method/query:



       String query = 'SELECT Id FROM Sales_Order__c ' + Label.UpdateArchivedOrderLinesWhere;

      global Database.QueryLocator start(Database.BatchableContext bc)
      return Database.getQueryLocator(this.query);



      ...and I'm now using a custom label to adjust my WHERE clause directly from production using timeframes on an indexed field (e.g. WHERE Actual_Order_Date__c > 2014-01-01T01:02:02z AND Actual_Order_Date__c < 2014-06-01T01:02:02z). I'll do this in chunks using as big of a timeframe as I can, while still maintaining a selective query.



      If you have other optimisation suggestions please feel free to post them, but this is now running successfully in prod against ±7mill big objects rows related to ±2mill various custom object rows.






      share|improve this answer
























        up vote
        3
        down vote










        up vote
        3
        down vote









        Although my query in the Start method was using an index, it wasn't qualifying as Selective in the dev console's Query Plan tool. The cost returned was 1.3, where SF says anything above 1 is no longer considered selective (see: https://help.salesforce.com/articleView?id=000199003&type=1)



        I've added the following to my start method/query:



         String query = 'SELECT Id FROM Sales_Order__c ' + Label.UpdateArchivedOrderLinesWhere;

        global Database.QueryLocator start(Database.BatchableContext bc)
        return Database.getQueryLocator(this.query);



        ...and I'm now using a custom label to adjust my WHERE clause directly from production using timeframes on an indexed field (e.g. WHERE Actual_Order_Date__c > 2014-01-01T01:02:02z AND Actual_Order_Date__c < 2014-06-01T01:02:02z). I'll do this in chunks using as big of a timeframe as I can, while still maintaining a selective query.



        If you have other optimisation suggestions please feel free to post them, but this is now running successfully in prod against ±7mill big objects rows related to ±2mill various custom object rows.






        share|improve this answer














        Although my query in the Start method was using an index, it wasn't qualifying as Selective in the dev console's Query Plan tool. The cost returned was 1.3, where SF says anything above 1 is no longer considered selective (see: https://help.salesforce.com/articleView?id=000199003&type=1)



        I've added the following to my start method/query:



         String query = 'SELECT Id FROM Sales_Order__c ' + Label.UpdateArchivedOrderLinesWhere;

        global Database.QueryLocator start(Database.BatchableContext bc)
        return Database.getQueryLocator(this.query);



        ...and I'm now using a custom label to adjust my WHERE clause directly from production using timeframes on an indexed field (e.g. WHERE Actual_Order_Date__c > 2014-01-01T01:02:02z AND Actual_Order_Date__c < 2014-06-01T01:02:02z). I'll do this in chunks using as big of a timeframe as I can, while still maintaining a selective query.



        If you have other optimisation suggestions please feel free to post them, but this is now running successfully in prod against ±7mill big objects rows related to ±2mill various custom object rows.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 1 hour ago

























        answered 1 hour ago









        Mark S

        465




        465



























             

            draft saved


            draft discarded















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsalesforce.stackexchange.com%2fquestions%2f237262%2fbatch-apex-request-running-too-long-can-i-optimise-my-class%23new-answer', 'question_page');

            );

            Post as a guest













































































            Comments

            Popular posts from this blog

            Long meetings (6-7 hours a day): Being “babysat” by supervisor

            Is the Concept of Multiple Fantasy Races Scientifically Flawed? [closed]

            Confectionery