0

I have a little problem with my batch:

List<String> idList;

public My_Batch(List<String> ocIdList) {
    idList= ocIdList;
}

global Database.QueryLocator start(Database.BatchableContext BC) {

    return Database.getQueryLocator('Select Id from Account where Id IN : idList ');

}

global void execute(Database.BatchableContext BC, List<Account> records) {         

    for(Account acc : records){

            String endpoint = 'https://urlendpoint/v1/';

            Http http = new Http();
            HttpRequest request = new HttpRequest();
            request.setHeader('Authorization', 'xxxxxxx');
            request.setEndpoint(endpoint);
            request.setMethod('GET');
            HttpResponse response = http.send(request); 

            CustomLog log = new CustomLog();                
            log.Status__c = 'Success';
            insert log;

            acc.log__c = TRUE;
            update acc;
}
}

This batch is called from the following INVOCABLE method:

            //.......staff......//
            CustomLog log = new CustomLog();                
            log.Status__c = 'Success';
            insert log;

            if(idList.size()>0){
                Database.executeBatch(new My_Batch(idList), 1);
            }

Most of the batch executions are successful but few of them return the following error : "You have uncommitted work pending. Please commit or rollback before calling out."

I know that the issue occurs when you first perform DML and then callout in the same transaction.

I don't understand two things:

1) In the execute of the batch I have first the callout and then the DML, so I shouldn't get any error.

2) Most of the executions are successful but few of them return the below error message. Why do they have different result?

Thanks in advance

1
  • I think the easiert way to look at it to setup a debug log of one of the error batches. It can be that one of the executions is firing something else. Can you post a debug log?
    – manjit5190
    Mar 26, 2020 at 17:42

1 Answer 1

2

The main problem in your code is that you're doing a DML and a callout inside of a for-loop. This is a fairly large no-no and is going to cause you to hit Governor limits very quickly. It is also going to cause the error you're seeing.

Here you need to do two things:

  1. Change your code to do one DML for the entire transaction, see below for an example

  2. If the provider of the call out cannot change its API so you can call it with more than one record, then ensure that you never call that code with more than 1 record (you get more than 1 callout per context, so you could perhaps get away with up to 10, which is the limit) but I would not recommend it. If the provider of the call allows you to call it with multiple records, then refactor your code to do so (in addition to what you did for Item 1)

Example for Item 1

List<CustomLog> logs = new List<CustomLog>();
for (Account acc : records) {
    //This is the code you can refactor for item 2
    String endpoint = 'https://urlendpoint/v1/';

    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setHeader('Authorization', 'xxxxxxx');
    request.setEndpoint(endpoint);
    request.setMethod('GET');
    HttpResponse response = http.send(request);
    //end place to refactor for item 2

    CustomLog log = new CustomLog();
    log.Status__c = 'Success';
    logs.add(log);

    acc.log__c = TRUE;
}

update records;
update logs;

NOTE

Make doubly sure you don't have any callouts being sent from triggers on either of those two objects, or on the finish method of this batch

4
  • Thank you for your support Sebastian. Looking at the logs, the problem is that it doesn't even go beyond the line with the callout. The log shows this exception "|EXCEPTION_THROWN|[43]|System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out " at this line: HttpResponse response = http.send(request); I don't see any |DML_BEGIN| log before the callout in my logs. Mar 27, 2020 at 14:10
  • Then we’re missing code in this context. The snippet you sent, on it’s own, shouldn’t fail. Try calling it directly and check Mar 27, 2020 at 14:24
  • Hi Sebastian, I've taken out the "update records" from the loop as you said, then I've called the batch directly but I have still same issue. It looks like that each execution is not considered as single transaction. As I said, I don't see any DML operation before the exception in the logs of each execution. Does a single execution store the operations made in the previous one? Mar 30, 2020 at 8:43
  • I’ll say this again, we’re missing important code. The code, as you posted it, shouldn’t fail. But there could be other factors that make it fail. Mar 30, 2020 at 14:40

You must log in to answer this question.

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