2

Requirement is to loop through set of records(could be in 1000's) and make a REST callout to process those records. I did went through multiple Q&A posted in the community but still stuck with it. I did tried to approach the solution per @Jeremy Nottingham on this post but not successful yet. Can we callout and chain a Queueable class?

public class Log_Processor {
public class FlowInputs{
    @InvocableVariable public List<Log__c> inputLog;
    @InvocableVariable public List<Log_Processor__mdt> logMetada;
    @InvocableVariable public String ProcessorType;
}    

@InvocableMethod(label='Process Exception')
public static void processLog(List<FlowInputs>logList){
    List<Log__c> logs = logList[0].inputLog;        
    List<Log_Processor__mdt> logMeta = logList[0].logMetada;
    if(logList[0].ProcessorType == 'Bulk'){ 
        ID jobID = System.enqueueJob(new PDS_Batch_Log_Processor(logs,logMeta));
    }

}

}

public class Batch_Log_Processor implements Queueable, Database.AllowsCallouts{

    public List<Log__c> logs;
    public List<Log_Processor__mdt> logMeta;

    public Batch_Log_Processor(List<Log__c>logList,List<Log_Processor__mdt>logMetaList){
        this.logs = logList;
        this.logMeta = logMetaList;
    }   

    public Batch_Log_Processor(){
    }

    public void execute(QueueableContext context) {             
            for(Integer i=0; i< logs.size(); i++){                
                    String extValue = logs[i].External_ID_Value__c;
                    if(String.isNotBlank(extValue)){
                        String extValue1= extValue.deleteWhitespace();
                        processPayload(logMeta[0].End_Point__c+extValue1,
                                       logMeta[0].Method__c,
                                       logs[i].Message__c,
                                       logs[i].Id                                     
                                      );
                        logs.remove(i);
                 }
            }     

    }

    @future(callout=true)
    public static void processPayload(String reqURL,String reqMethod,
                                      String reqBody,String logID){              
            HttpRequest httpRequest = new HttpRequest();  
            httpRequest.setMethod(reqMethod); 
            httpRequest.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID()); 
            httpRequest.setHeader('Content-Type','application/json');
            httpRequest.setEndpoint(reqURL); 
            httpRequest.setBody(reqBody);        
            Log__c logToUpdate = new Log__c();
            logToUpdate.Id = logID;
            logToUpdate.Processed_Date__c = System.now();            
            try {                
                     Http http = new Http();   
                     HttpResponse httpResponse = http.send(httpRequest);       
                     logToUpdate.Stack_Trace__c = httpResponse.getBody();                    
                } catch( System.Exception e) {
                    logToUpdate.Stack_Trace__c = String.valueOf(e);                    
                    System.debug('ERROR: '+ e);
                }
            //update logToUpdate;
            System.enqueueJob(new PDS_Batch_Log_Processor());
    }     

I still get the error "Too many future calls:51". I am not sure if I misunderstood the post or I am not the doing the chaining properly.

Derek F
  • 61,401
  • 15
  • 50
  • 97
user28452
  • 441
  • 15
  • 36

2 Answers2

2

You don't need to toggle between Queueable/future any longer. That's an ancient bug that has since been fixed (as mentioned in the answer in my question you linked). Here's how you can fix it.


public void execute(QueueableContext context) {
    Log__c[] logsToUpdate = new Log__c[0];
    while(logs.size() > 0 && logsToupdate.size() < 100) { // Max 100 callouts allowed here
        Log__c nextLog = logs.remove(0);
        logsToUpdate.add(processPayload(logMeta[0].End_Point__c+extValue1, logMeta[0].Method__c, nextLog.Message__c, nextLog.Id));
    }
    update logsToupdate;
    if(logs.size() > 0) {
        System.enqueueJob(this);
    }
}
public static void processPayload(
        String reqURL,
        String reqMethod,
        String reqBody,
        String logID) {
    HttpRequest httpRequest = new HttpRequest();  
    httpRequest.setMethod(reqMethod); 
    httpRequest.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID()); 
    httpRequest.setHeader('Content-Type','application/json');
    httpRequest.setEndpoint(reqURL); 
    httpRequest.setBody(reqBody);        
    Log__c logToUpdate = new Log__c();
    logToUpdate.Id = logID;
    logToUpdate.Processed_Date__c = System.now();            
    try {                
        Http http = new Http();   
        HttpResponse httpResponse = http.send(httpRequest);       
        logToUpdate.Stack_Trace__c = httpResponse.getBody();                    
    } catch( System.Exception e) {
        logToUpdate.Stack_Trace__c = String.valueOf(e);                    
        System.debug('ERROR: '+ e);
    }
    return logToUpdate;
}     

As you can see, we perform up to 100 callouts, then update the logs with the received information, and if we have any left to do, we process the next 100 logs asynchronously. By this chain, we can process thousands of records efficiently.

sfdcfox
  • 489,769
  • 21
  • 458
  • 806
  • @sfdcfox: Thanks for the response. I didn't follow this line. Log object doesn't have endpoint, requestMethod as those are coming from custom metadata object. I am passing all the input parameters as string to processpayload method. logsToUpdate.add(processPayload(logMeta[0].End_Point__c+extValue1, logMeta[0].Method__c, nextLog.Message__c, nextLog.Id)); – user28452 Nov 02 '21 at 03:30
  • @sfdcfox: Thank you again. I am upvoting as this helped me to move in the right direction. – user28452 Nov 02 '21 at 21:59
1

Thanks to @Nathanael Schmolze at this post Calling batch class multiple times in a row I was able to follow similar pattern to the posted question.

public class PDS_Batch_Log_Processor implements Queueable, Database.AllowsCallouts{

public List<Log__c> logs; public List<Log_Processor__mdt> logMeta; public Integer logIdx;

public PDS_Batch_Log_Processor(List<Log__c>logList,List<Log_Processor__mdt>logMetaList){ this.logs = logList; this.logMeta = logMetaList; this.logIdx = 0; }

public void execute(QueueableContext context) {
if(this.logIdx == this.logs.size()) return;

 if(this.logIdx == this.logs.size()){
    this.logIdx = 0;
 }
 else{
     Log__c currentLog = logs[this.logIdx];            
     String extValue = currentLog.External_ID_Value__c;             
     String extValue1= extValue.deleteWhitespace();

    HttpRequest httpRequest = new HttpRequest();  
    httpRequest.setMethod(logMeta[0].Method__c); 
    httpRequest.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID()); 
    httpRequest.setHeader('Content-Type','application/json');
    httpRequest.setEndpoint(logMeta[0].End_Point__c+extValue1); 
    httpRequest.setBody(currentLog.Message__c);        
    Log__c logToUpdate = new Log__c();
    logToUpdate.Id = currentLog.Id;
    logToUpdate.Processed_Date__c = System.now();
    try {                
             Http http = new Http();   
             HttpResponse httpResponse = http.send(httpRequest);       
             logToUpdate.Stack_Trace__c = httpResponse.getBody();                    
        } catch( System.Exception e) {
            logToUpdate.Stack_Trace__c = String.valueOf(e);                    
            System.debug('ERROR: '+ e);
        }
        update logToUpdate;             
     System.debug('@@currentLog_Id==&gt;'+currentLog.Id);
     this.logIdx++;
     System.enqueueJob(this);
 }

}

}

user28452
  • 441
  • 15
  • 36