2

I have a button that calls a very complex feature that caused salesforce limits to be reached. I modified the method in asynchronous (@future) and added two states: "waiting" and "not processed".

The first state is used to indicate the time between the click on the button and the execution of the @future method. The second is used in case of problems in the execution of the method.

My problem is the concurrency, if a user opens the same object in two tabs and clicks on the button in both tabs, execute both methods and insert double the records.

I included a check in the future method that before starting check if they have already been created but it does not work. I always get double insertion.

How can i solve this problem?

2 Answers 2

4

You can use a locking statement to help combat this problem. Basically, you just need to take whatever your original query was and add FOR UPDATE to the end of it; if any records are in flight but not committed, your future method will wait for them to finish. Note that if you are going to need more than about 5 seconds, you'll want to put the locking statement in a while-try-catch loop like this:

SObject[] records;
while(records == null) {
  try {
    records = [SELECT ... FROM ... FOR UPDATE];
  } catch(QueryException e) {
  }
}

This loop can wait up to about five minutes depending on how many queries you need for the rest of your transaction, which should be more than enough time for the other concurrent future method to finish.

0

You can use Locking statement as sfdcfox mentioned, for me it has caused more issues like RACE condition and longer transaction time and the dreaded "Concurrent Limit Exceeded"

I would like to suggest an approach which might sound weird but works wonders.

In your future method Querry ApexAsyncJobs to see if a future method is queued or running by same user for same future method. If not running then querry database and see if that record exists. If both conditions are true then do that record's insert.

public class jobClass

{

  @future
  public static void doMyJob(){

    Set<String> stringSet =new Set<String>();
    stringSet.add('Holding');
    stringSet.add('Queued');
    stringSet.add('Preparing');
    stringSet.add('Processing');


     List<AsyncApexJob> apexJobs =[SELECT ApexClassId,CreatedById,MethodName,
                JobType,status FROM AsyncApexJob where JobType='Future' 
                and MethodName='doMyJob' and status NOT in :stringSet and  CreatedById=:UserInfo.getUserId()
                ORDER BY createdDate DESC LIMIT 1];

    if(apexJobs.isEmpty()){
        //No job is queued or running by the same user

        //Querry if that record was already inserted or not

        List<Record__c> listRecord =[Select bla bla];

        if(listRecord.isEmpty()){
            //This means we dont have a record inserted as well a we dont have any future method pending

            INSERT MYRECORD;
        }
    }
  }

}

Source: https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_asyncapexjob.htm

You must log in to answer this question.

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