[SalesForce] You have uncommitted work pending. Please commit or rollback before calling out error

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?

Best Answer

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());
    }
Related Topic