0

I have a scheduled batch, that takes all cases with field isFailed__c true in the start method:

global class UpdateCases implements Database.Batchable<sObject>, Schedulable, Database.AllowCallouts {
    global Database.QueryLocator start(Database.BatchableContext bc) {
        String query = 'SELECT id, isFailed FROM Case WHERE isFailed__c = true';

        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext bc, List<Case> records) {
        List<Case> cases = new List<Case>();
        for(Case c : records) {
            Boolean isFailed = CalloutOne.callout();
            if(!isFailed) {
               cases.add(c);
            }
        }
        if(!cases.isEmpty()) update cases;
    }

    global void finish(Database.BatchableContext bc){
        // execute any post-processing operations
    }

    global static void execute(SchedulableContext ctx) {
        try {
            Database.executeBatch(new UpdateCases (), 50);
        } catch (Exception e) {throw ErrorLogEventHandler.standardExceptionLog(e);}
    }
}

The CalloutOne class has the following:

public with sharing class CalloutOne {
    public static Boolean callout() {
        ....
        Boolean isFailed = true;
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        ....
        try {
            HttpResponse respHttp = http.send(req);
            if(respHttp.getStatusCode() == 200) {
                isFailed = CalloutTwo.callout();
            }
            return isFailed;
            CalloutLogger.insertLog();
        } catch(Exception e) {
            ....
        }
        return isFailed;
    }
}

The CalloutTwo class:

public with sharing class CalloutTwo {
    public static Boolean callout() {
        ....
        Boolean isFailed = true;
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        ....
        try {
            HttpResponse respHttp = http.send(req);
            CalloutLogger.insertLog();
            if(respHttp.getStatusCode() == 200) {
                isFailed = CalloutTwo.callout(...);
                return false;
            }
        } catch(Exception e) {
            ....
        }
        return true;
    }
}

Unfortunately, the endpoints (not designed by us, but 3rd party external services) do not work with list requests. So I have to send one request at time which is normally the case as the callout is triggered by a button on case details page.

But the problem arise with Batch. I have to do callouts in the for loop and as each execute transaction allows 100 callouts a batch size of 50 is used. But unfortunately because we have loop and DML operation i.e., insertion of callout logger record, I am getting You have uncommitted work pending. Please commit or rollback before calling out.

I was thinking of calling the CalloutOne via a Queueable class, but I read that inside the batch we can have only one enqueueJob.

Is my only option: using a batch size of 1? If so, what are possible drawbacks o pitfalls I might face?

2

The problem isn't really the remote API - it's the design of your CalloutOne and CalloutTwo classes. With the logging DML in the position that it is, you can never call these classes more than once in a transaction.

Consider a different pattern. For example, you could pass in a Map<Id, List<Some_Logging_Record__c>> instance and let both callout classes append their logs to the relevant collections. Then your outermost logical layer (the batch class) can take responsibility for inserting the logs once the last callout for the transaction has taken place.

2
  • I think I understand, I will try it and let you know. Thanks. – nSv23 Mar 15 at 23:13
  • Simple, smart and elegant solution. Once again thank you. – nSv23 Mar 16 at 9: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.