3

I see similar post to this issue but I can't find an exact match.

Present Design:

After Insert or update trigger calls 1st apex class > Future method 1st class calls 2nd apex class > Method http.send

I want to log the result to a custom object and the first time succeeds but second attempt fails and write you have uncommitted changes.

I believe this issue is the write to the custom object is a DML operation and I need to move this to be outside the callout. However, based on how the design is currently strucutred I am not sure what is the best way to do it.

My limitations I have encountered so far are: Trigger will not fire the first apex class without the @future I can't call one future from another future.

How can I commit the log in a separate transaction given present design and limitations?

1

2 Answers 2

4

If you log after 1st callout you cannot do a callout again in same transaction, as Callouts after DML are not allowed. You have to sperate your transaction, As you are in Future you cant call another future, but that's where successor of Future comes in picture.

You need to use Queuable Apex.

Queueable apex is just like future but with few advantages.

  1. Can Call Queableable apex from queuable apex
  2. Can pass complex Data strcutures(Future accepts primitive types only)
  3. Can chain queuable apex till infinity
  4. Can track the status of Queuable apex as we get ID when you enqueue it.

    https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_queueing_jobs.htm

Other option you have is , do both callouts and then log both their results in last. ie Callout Callout and then DML.

Edit: Pseudo Code as requested

public MyQueuableClass1 Implements Queueable,Database.allowCallouts{

   public void execute(QueuableContext qc){

      //Do Callout
      //Do DML For Logging

        System.enqueJob(new MyQueuableClass2()); // Dont forget to pass your Parms
   }
}

public MyQueuableClass2 Implements Queueable,Database.allowCallouts{

   public void execute(QueuableContext qc){

      //Do Callout
      //Do DML For Logging

        System.enqueJob(new MyQueuableClass3()); // Dont forget to pass your Parms
   }
}




//Calling Queuable from Trigger.or anwhere 
    Trigger AccountTrigger on Account(after update) {
        System.enqueJob(new MyQueuableClass1());
    }
4
  • I agree Queueable apex does seem to be salesforce answer to the above type of scenario but this is our first attempt at doing something like this and we were not aware of this until now. I suppose I was just hoping someone might have run into this and was a simple fix.
    – Daryn
    Aug 10, 2018 at 13:45
  • I have been using queueable apex for last 2-3 years, And they are more trustworthy reliable than future, In fact I have stopped using future now... Totally worth it Aug 10, 2018 at 13:46
  • Given the design I presented how would you do it with Queueable ? Is it an easy conversion?
    – Daryn
    Aug 10, 2018 at 13:47
  • @Daryn I have provided a pseudo code so that you can have a look at the architecture.. Aug 10, 2018 at 13:53
2

In your future method, do all the callouts first, then the DML operation. It sounds like you have a DML operation in a loop, like this:

for(Object value: values) {
  result = doCallout(value);
  saveResult(result);
}

When what you need is this:

sobject[] results = new sobject[];
for(Object value: values) {
  results.add(doCallout(value));
}
saveResults(results);

What this means in your code is up to you, just remember to save all your DML statements until the end.

You must log in to answer this question.

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