5

I have a process builder flow which is triggered by opportunity creation and updates, and that flow executes a bit of apex code. It POSTs to an external service and sets a field on the opportunity:

public class OpportunityProcessor {

  @InvocableMethod
  public static void notifyThirdPartyApi(List<Opportunity> opportunities) {
    for (Opportunity opportunity : opportunities) {

      // make asynchronous http callout
      postToSomeService(opportunity.Id);

      // update opportunity record
      opportunity.Custom_Checkbox__c = true;
      update opportunity;
    }
  }

  @future(callout=true)
  public static void postToSomeService(Id opportunityId) {
    HttpRequest req = new HttpRequest();
    req.setEndpoint('http://service.com/some/resource');
    req.setMethod('POST');
    req.setHeader('Content-Type', 'application/json');
    req.setBody('{"opportunity_id": ' + opportunityId + '}');

    Http http = new Http();

    //
    // What happens to the above method when
    // a System.CalloutException is thrown here? |
    //                     vvvvvvvvvv------------+
    HttpResponse res = http.send(req);
  }
}

What happens if/when that HTTP callout code throws an exception?

Will the opportunity.Custom_Checkbox__c still get updated? Or will the transaction be rolled back?

My intuition says that the transaction will not get rolled back, and the opportunity.Custom_Checkbox__c will stay updated to true. This is because postToSomeService is asynchronously queued for later execution, which means the code in notifyThirdPartyApi will probably be long done by the time the callout code is executed.

But I don't actually know, and I can't find any answers on this page, this page, or this page documenting @future methods.

2

Your intuition is correct; the future method isn't even called until after the transaction that calls the method has committed. That means the box will be checked, even though the callout may eventually fail. Anything that happens in the future method will have absolutely no impact on the previous transaction.

5
  • Thank you for the quick response! Did you learn this from a piece of documentation somewhere? Or is this from experience? – kdbanman Feb 21 '17 at 18:00
  • @kdbanman I don't know of a document that spells this out explicitly, but I definitely know from experience. Fun fact: you can actually cancel a future method that's pending by using Database.rollback, just like any other type of record. This behavior also avoids awkward scenarios like using allOrNone to allow partial retries; instead of 2 or 3 calls, you still end up with only one version of the future method. – sfdcfox Feb 21 '17 at 18:16
  • For future readers, should there be any, I've also tested this now. I threw an exception explicitly inside an @future method, and the transaction from where that method was called was unaffected. Inserted and updated data was just fine. – kdbanman Feb 21 '17 at 23:48
  • @kdbanman did you try with an exception that cannot be caught? – Phil B Apr 8 '19 at 18:30
  • @PhilB I assure you, any exception you throw inside the future method won't affect the parent transaction, even if it cannot be caught. – sfdcfox Apr 9 '19 at 7:05

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.