1

I was trying to test if Async opens up a new thread or is tied to the main thread and the result was very interesting.

Consider this class:

public with sharing class TestClassTest {

    public static void testFutureException(){
        testFuture();
        throw new FutureException('Future exception came');
    }

    @Future
    public static void testFuture(){
        ARecord__c aRecord = new ARecord__c();
        aRecord.Name = 'Test Future';
        insert aRecord;
        System.debug(aRecord);
    }

    public class FutureException extends Exception {

    }

}

Here I have called a future first and trying to insert a record, and then after that throwing an exception from Main thread, but interestingly the future does not even get called as its not shown in Apex Job. Even if I put a debug statement inside future job it does not get printed. So My assumption here is, future or any async job waits for the main thread to finish its transaction and then calls the async functions

But even this assumption fails when I do this:

public with sharing class TestClassTest {

    public static void testFutureException() {
        testDML();
        webServiceCallout();
    }
    
    public static void webServiceCallout() {
        HttpRequest httpRequest = new HttpRequest();
        httpRequest.setEndpoint('https://jsonplaceholder.typicode.com/todos/1');
        httpRequest.setMethod('GET');
        Http http = new Http();
        HttpResponse httpResponse = http.send(httpRequest);
        System.debug(httpResponse.getBody());
    }

    @Future
    public static void testDML() {
        ARecord__c aRecord = new ARecord__c();
        aRecord.Name = 'Test Future 1234';
        insert aRecord;
    }

    public class FutureException extends Exception {

    }

}

Here I am doing a DML in future, but as per previous assumption, DML should not be executed until my callout is successful. But that is not how it works here. Here it throws an exception saying Error on line 17, column 1: System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

That means the DML did happen but in Apex Job I still cant see this future job and neither can I see any debug logs if I put a System.debug inside future method.

Is there an inbetween layer somewhere between my org instance and Salesforce server where this happens and does not get logged in my org debug logs? And why is future/Queueable or any async process getting affected by the main process/thread?

1

Threads are generally not a useful framework to use to think about Asynchronous Apex, in my opinion; the transaction is what is critical.

So My assumption here is, future or any async job waits for the main thread to finish its transaction and then calls the async functions

This is basically accurate although the nomenclature of threads is not quite right. Asynchronous Apex is not enqueued for execution until the transaction in which it is enqueued is committed. Enqueuing Asynchronous Apex is a "DML-equivalent" operation; it persists to the database and is therefore transactional.

This behavior is documented; see for example Future Methods in the Apex Developer Guide:

Future jobs queued by a transaction are not processed if the transaction rolls back.


Here I am doing a DML in future, but as per previous assumption, DML should not be executed until my callout is successful. But that is not how it works here. Here it throws an exception saying Error on line 17, column 1: System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out

Enqueuing the future job is itself a DML operation. That's what's blocking the callout, not the DML operation in the future method, which has not in fact yet executed.

5
  • So will adding any async operation be counted in DML? For eg: Future, Queueable, Batch ? Sep 2 '21 at 3:28
  • 1
    Yes, that’s it exactly.
    – David Reed
    Sep 2 '21 at 3:33
  • This answers all my questions which was confusing me from yesterday, since I executed the snippets above. Thanks for detailed explanation. Sep 2 '21 at 3:37
  • So considering above logic, there is no way to insert record in DB before a callout. For eg: If we have to add a log file before callout. The only option is to log that after callout , right? Sep 2 '21 at 6:02
  • Or use non transactional Platform Events to bounce into a new transaction, yeah
    – David Reed
    Sep 2 '21 at 12:40

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.