[SalesForce] How to solve Async Future Method Inside Loops Issue

Can any one please help me solving this,

The below trigger will create an guid(random_string) and it will be passed to an controller and from controller i'm hitting the Post the data to our URL. Which working fine, But when I submitted for "Force.com Security Scanner Results". I'm getting an error.

Query: Async Future Method Inside Loops It is critical to write your
Apex code to efficiently handle bulk, that is, many records at a time.
This is also true for asynchronous Apex methods (those annotated with
the @future keyword). Even though Apex written within an asynchronous
method gets its own independent set of higher governor limits, it
still has governor limits with respect to the number of @future
invocations allowed in a single Apex transaction. References:
http://wiki.developerforce.com/index.php/Apex_Code_Best_Practices
http://wiki.developerforce.com/index.php/Governors_in_Apex_Code Async
Future Method Inside Loops result path 1:

Path Id: 1674181361 Object: sendrequest in file:
triggers\setAppKey.trigger L 13:
App_keygenrt.sendRequest(dac.App_Key__c,dac.Name); Object:
sendrequest in file: classes\App_keygenrt.cls L 3: public
static void sendRequest(String app_key,String app_name){

Trigger code

trigger setAppKey on app__c (before insert, after insert) {
    if (Trigger.isBefore){
        For (app__c dac: Trigger.new){
            Blob b = Crypto.GenerateAESKey(128);
            String h = EncodingUtil.ConvertTohex(b);
            String appkey = h.SubString(0,8)+ '-' + h.SubString(8,12) + '-' + h.SubString(12,16) + '-' + h.SubString(16,20) + '-' + h.substring(20);
            system.debug(appkey);
            dac.App_Key__c = appkey;
        }
    }
    if (Trigger.isAfter){
        For (app__c dac: Trigger.new){
         App_keygenrt.sendRequest(dac.App_Key__c,dac.Name);
        }
    }
}

Controller

public class App_keygenrt {   
        @future
        public static void sendRequest(String app_key,String app_name){
            demoTestWSCallouts call = new demoTestWSCallouts();
            if (!System.Test.isRunningTest()) {
                  call.invokeWS(app_key,app_name);
            }
        }     
}

Controller for Posting data

public class demoTestWSCallouts {   
   public HttpResponse invokeWS(String app_key,String app_name) {
      String Org_ID = UserInfo.getOrganizationId();
        HttpRequest req = new HttpRequest();
        HttpResponse res = new HttpResponse();
        Http http = new Http(); 
        req.setMethod('GET' ); // Method Type
        req.setEndpoint('https://------------'); // Server Url
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded'); // Content Type
        req.setBody('app_key=' + EncodingUtil.urlEncode(app_key, 'UTF-8') + '&app_name=' + EncodingUtil.urlEncode(app_name, 'UTF-8')+ '&Org_ID=' + EncodingUtil.urlEncode(Org_ID, 'UTF-8')); // Request Parameters
        req.setCompressed(true);        
        res = http.send(req);
        System.assertEquals(200, res.getStatusCode());
        return res;
        }
}

Best Answer

When a trigger executes each invocation may have up to 200 records. So, if 200 or more app__c records were updated in a single transaction the following loop would run 200 times for the first invocation of the trigger.

if (Trigger.isAfter){
    For (app__c dac: Trigger.new){
     App_keygenrt.sendRequest(dac.App_Key__c,dac.Name);
    }
}

That's 200 asynchronous calls being made to your future method, that will each result in a single callout.

Instead, build up a collection of all the requests that need to be made and pass it off in a single call to the future method.

Now the problems becomes passing the data the the HTTP Callout. In the ideal world the web service would be modified to work on multiple records at once.

If that isn't possible, you will need to work within the Callout Limits and Limitations.

Related Topic