13

I have a trigger after insert on some arbitrary entity:

trigger OpportunityTrigger on Opportunity (after insert) {
    // not bulkified for the sake of simplicity

    ID jobID = System.enqueueJob(new CalloutJob());
    insert new Callout__c(JobID__c = jobID, Payload__c = Trigger.new[0]));
}

and the CalloutJob class that implements Queueable:

public class CalloutJob implements Queueable {
    public void execute(QueueableContext ctx) {
        // find the related Callout record
        Callout__c callout = [Select Id, Payload__c From Callout__c Where JobID__c = :ctx.getJobId()];

        // and do some fancy processing based on the callout object
    }
}

Is it guaranteed that the insert DML operation runs before the CalloutJob is executed? In other words - is it guaranteed that the SELECT statement always finds the related callout record?

I did some initial tests on ~20 records and it seems just fine. But I couldn't find any unambiguous official documentation on that.

  • 2
    I recollect a posting that made the point that the creation of an asynchronous operation was itself a database insert so the asynchronous operation could not start before the current transaction was committed. But that is more of an observation about the probable implementation than a definite API contract. – Keith C Jun 13 '15 at 9:24
12

Transactions have to be fully ACID compliant. The "C" stands for consistency, and this is where transaction control comes in. In order for your Queueable to execute, it has to be guaranteed that the transaction that placed the Queueable into the queue has committed. This means that your Queueable cannot run before the transaction has been fully committed to the database, around the same time frame where emails are actually sent out and the SystemModStamp field is finally updated. Therefore, in order to be consistent, Queueables must run after the transaction completes. This is a guarantee. If the transaction aborts for any reason before the final commit, no emails are sent out, no records are saved, and no Queueable, batchable, or scheduled items will persist.

8

That's an interesting question. With the queueable execution being asynchronous, could there be a particular timing where the queued job would start executing before the corresponding Callout__c record is inserted in the transaction that queued the job?

My understanding is that the job isn't actually enqueued ready for execution until the DML operations have been committed to the database. Have a look at Triggers and Order of Execution. Notice that records are saved to the database in step 6, but not actually committed until step 19. So if your transaction was to get rolled back, the enqueued job would also get rolled back without executing.

Even if it was possible, the Queueable job would need to start in a different process and query the Callout__c record before the prior transaction could create it. To get that sort of timing would require a delay between the job being enqueued and the insertion. A callout could create that sort of delay, but isn't an option in the trigger context.

The question Transaction isolation in Salesforce also raises the interesting question of if SOQL can do dirty reads on uncommitted records.

  • The commit/rollback explanation makes a lot of sense to me. But it's based on the foundation that Queueable jobs are persisted with the same transaction rules as for any other entity. And that foundation is still our assumption. – Paweł Rychlik Jun 13 '15 at 12:45
  • @PawełRychlik A failed transaction must leave the database in the exact same state it found it. For example, Messaging.sendEmail won't send any emails if the transaction fails. This is a guarantee by the platform. If even a single side effect occurred because of a failed transaction, it could leave data in an inconsistent state. – sfdcfox Jun 13 '15 at 12:51
  • 2
    And, SOQL can't perform reads on uncommitted data at all. I've tested this extensively. It's this very reason why locking statements exist-- it allows transaction B to pause and wait for transaction A to complete any updates to a record to make sure that B has the latest information and doesn't clobber A. I've done some testing on the locking mechanism as well. Interesting stuff, really. – sfdcfox Jun 13 '15 at 12:55
  • The uncommitted DML operation explanation is consistent with the SDFC_501 video series available on YouTube and iTunes. I think Order of Execution and DML is covered in Video #7 or #8 in the series. – crmprogdev Jun 13 '15 at 12:57

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.