25

I just ran into a huge gotcha in Salesforce: the 24 hr limit on # of future calls - cf. http://www.sundoginteractive.com/index.php?/sunblog/posts/governor-limit-gotchas-part-1-the-future-call/.

I have the following questions about how to handle this:

  1. How can I determine how many future calls have been made in a 24 hour period? I need to have a trigger check first so that it doesn't cause the overall upsert operation to fail on SF records if I'm over the limit. I saw a post on Developerforce boards saying it was stored in AsyncApexJobs object, but I didn't see how to query it in the API docs.

  2. What's the best way to have a custom script that I do to update a bunch of records in SF ensure that it is not making any future calls? Is there a way I could set some kind of global variable in my script that i could then reference in all the triggers so they wouldn't make future calls if I'm doing a mass update (it's an update of over 2000 records).

Currently I am in a really awkward situation where none of the leads from my external systems are getting added b/c of the future call failing in a trigger set to run whenever a Lead is upserted.

Here is the script that does the sync to Salesforce: https://github.com/techmission/mdl_custom_reports/blob/master/sf_student_sync.php. It is using an upsert function that leverages the PHP Toolkit, and the function is in the same Github repo, in sf_libs.inc

The code of the trigger is here:

trigger CityvisionUpsertLead on Lead (after insert, after update) {
   // score & qualify all leads
   // methods execute in future, so as to be non-blocking for save, and so tests will pass
   // confirm we are not in a future context or a batch context (i.e., when doing a bulk update)
   if(!System.isFuture() && !System.isBatch()) { 
     Set leadIds = new Set(); // leads to score and qualify - currently filtered in the methods, not here
     // iterate over all the leads
     for (Lead l : Trigger.new) {
        // add all to the pool for scoring and qualification
        leadIds.add(l.Id);
     }
     // invoke the methods for scoring and qualifying leads
     // if this is not being called from a future method context 
     if(!leadIds.isEmpty()) {
       CityvisionUtils.scoreLeads(leadIds);
       CityvisionUtils.qualifyLeads(leadIds);
    }
  }
}
29

(1) To determine the number of Future calls run in the past 24 hours, you can use this utility method:

public static Integer GetNumFutureCallsInLast24Hours() {
    return [select count() from AsyncApexJob 
            where CreatedDate >= :Datetime.now().addHours(-24) 
            and JobType = 'Future'];

(2) To dynamically run methods in a future context ONLY if you have future calls available, you could leverage a "dispatcher" method framework that would run your code in a future context only if you have not surpassed the 24-hour-calls Limit and Call Stack Limit yet:

// Can we run future calls right now?
public static boolean CanUseFutureContext() {
    boolean callStackLimitExceeded = Limits.getFutureCalls() >= Limits.getLimitFutureCalls());

    if (!callStackLimitExceeded) {
        // Check 24-hour rolling window limit
        // of 200 * # of Salesforce, Salesforce Platform, or Force.com One App Licenses
        // *NOTE 1*: These queries may be cacheable depending on your situation
        // *NOTE 2*: If you have not used up all of your allocated licenses,
        // you will have more available licenses than this, so you may need to adjust.
        // However, you can't use SOQL to determine 
        //how many AVAILABLE licenses you have, 
        // only how many have actually been assigned to users
        return GetNumFutureCallsInLast24Hours() < getMaxFutureCallsAllowed();
    } else return false;
}

public static Integer getMaxFutureCallsAllowed() {
    Integer usersCount = [
        SELECT
            COUNT()
        FROM
            User 
        WHERE
            Profile.UserLicense.LicenseDefinitionKey IN ('SFDC','AUL','PID_FDC_FREE')];
    return Math.max(250000, usersCount*200);
}

// Main entry point to your method: call this from a trigger, other class, etc.
public static void RunMyLogic(params) {
    if (CanUseFutureContext()) futureRunMyLogic(params);
    else RunMyLogicMain(params);
}

private static void RunMyLogicMain(params) {
   /* Your logic here */
}

@future
private static void futureRunMyLogic(params) {
   RunMyLogicMain(params);
}
  • This code is great. Exactly what I was looking for. – Evan Donovan Oct 16 '12 at 18:42
  • One follow-up question: In relationship to part 2 of my original question, is there a way to set a global variable from the SOAP API, or in some other way indicate that I am in a different context and so the regular triggers should not run? – Evan Donovan Oct 16 '12 at 19:18
  • Nevermind - Mikey's answer helped clarify how best to do this. – Evan Donovan Oct 16 '12 at 19:20
  • Nice recipe. The lengths you need to go to determine available future calls is a bit questionable, imo, but you've carved out a reasonable path to getting the answer. Thanks for sharing it. – Adam Dec 13 '12 at 16:53
  • @Adam , definitely agree about the lengths being questionable. Vote the heck out of this idea Visibility into 24-hour future call consumption – zachelrath Dec 13 '12 at 20:35
5

* [EDIT: Just read through the Developer Boards post, and then confirmed this is only within the context of the future call stack, and not the 24 hour limit, I thought for sure there was a method that exposed the 24 hour limit...let me dig around a bit more.]

For #1, take a look at the Limits methods, specifically Limits.getFutureCalls() and Limits.getLimitFutureCalls(). Depending on how your code is structured, you could branch off and only perform some logic if you've got X number of Future Calls available.*

Salesforce Limit Methods

For #2, depending on how your org is structured, one thing we do is have all of our bulk loads done by a specific API Only Profile (perhaps a SysAdmin in a smaller org). We typically have logic in place in Workflow Rules/Triggers/other that won't cause logic to fire if this Profile is performing the bulk update.

One other thing you could look at would be having Custom Setting(s) to store a variable on whether or not to execute the logic, that your code could check. This could easily be turned on and off prior to doing any bulk loads, but depending on how it's setup, could also remain off if a user is performing an update via the UI during this same load window. That's why I would recommend scoping the logic to a user/profile.

Salesforce Custom Settings

  • Oh, I see what you mean about the Custom Settings - that's probably the best way for me to turn off. Maybe just checking to see if my API user is doing the updates would be adequate as well. Sorry I accepted Zach's answer because it was more detailed, but yours is very helpful too. – Evan Donovan Oct 16 '12 at 19:19

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.