2

Our initial goal is to have a trigger fire a callout with a large amount of inserted records.

I have a trigger, which triggers a future callout from an apex class.

Trigger:

trigger pushMessageTrigger on TMP_Push_Messages__c (after insert) {
    Plexure.PushMessage(Trigger.newMap.keySet());
}

Apex class:

global class Plexure {

  @future(callout=true)
  public static void PushMessage(Set<Id> pushMessageIds) {
   List<TMP_Push_Messages__c> messages = [select consumerId__c, messageId__c from TMP_Push_Messages__c where Id IN :pushMessageIds]; 
   for (TMP_Push_Messages__c message: messages) {
       try {
            // Generate guid
            Blob b = Crypto.GenerateAESKey(128);
            String h = EncodingUtil.ConvertTohex(b);
            String guid = h.SubString(0,8)+ '-' + h.SubString(8,12) + '-' + h.SubString(12,16) + '-' + h.SubString(16,20) + '-' + h.substring(20);

            Http http = new Http();
            HttpRequest req = new HttpRequest();

            req.setEndpoint('https://xxxxxxxxxxxxx'+message.consumerId__c+'/xxxxxxxxxxxx');
            req.setBody('{"messageId" : "'+message.messageId__c+'", "requestId" : "'+guid+'"}');
            req.setMethod('POST');
            req.setHeader('Content-Type', 'application/json');
            req.setHeader('Authorization', 'xxxxxxxx');
            req.setHeader('x-vmob-authorization', 'xxxxxxxxxxx');
            HTTPResponse res = http.send(req);

        } catch (Exception e) {
            System.debug('Got an exception');
            System.debug(e);
        }
   }  
}

I found out that it is not possible to trigger callout methods unless it has ‘@future(callout=true)’ notation and it’s not possible to pass Lists into @future methods. (Also, looked up that it’s a better practice to use ‘Trigger.newMap.keySet()’ in the trigger from this article: https://developer.salesforce.com/page/Best_Practice:_Use_future_Appropriately).

When inserting more than 100 records, an error occurs ‘Too many callouts: 101’ in the log, and I am aware that it happens because of the 100 callout limitation. Please assist with a possible solution/workaround on how avoid hitting the limit?

2 Answers 2

2

Instead of calling a single @future(callout=true) method you can start a batchable running where the number of records processed in each execute call can be limited to be below the governor limit:

public class Plexure {

    public static void PushMessage(Set<Id> pushMessageIds) {
        Database.executeBatch(new MyBatchable(pushMessageIds), 50);
    }
}

A batchable breaks work for many records up into multiple limited size lists of records. Your TMP_Push_Messages__c query goes into the start method and the rest of the code goes into the execute method.

You also need to add the Database.AllowsCallouts marker interface:

public class MyBatchable implements Database.Batchable<sObject>, Database.AllowsCallouts {

    private Set<Id> pushMessageIds;

    public MyBatchable(Set<Id> pushMessageIds) {
        this.pushMessageIds = Set<Id> pushMessageIds;
    }

    public Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator([
            select consumerId__c, messageId__c
            from TMP_Push_Messages__c
            where Id IN :pushMessageIds
        ]);
    }

    public void execute(Database.BatchableContext bc, List<TMP_Push_Messages__c> scope) {
        ...
    }

    public void finish(Database.BatchableContext bc) {
    }
}
2
  • Thank you for a quick reply Keith C. Just to clarify: the trigger stays unchanged and all the logic starting from the for loop in goes inside execute in MyBatchable? (for(TMP_Push_Messages__c message: scope){...}). Jan 16, 2018 at 14:30
  • @bad_student Yep that's it. (I advise you to check the status code returned in HTTPResponse as it is easy for web service calls to fail from time to time.)
    – Keith C
    Jan 16, 2018 at 15:30
0

From an Architectural perspective, It is not a good practice to do a single HTTP Callout per record but if it is a business need validated by an architect because of different constaints (?...) You can use the Queueable Apex to process callouts Asynchrounously from a Trigger.

To avoid reaching 101 callouts, you can chain executions (look at Chaining Jobs in the documentation) in onder to avoid processing more than 100 record per execution.

2
  • Fully agree with you Akram G, but unfortunately this is a clients API. Thank you for your input. Jan 16, 2018 at 14:48
  • Queueable and chaining are good to consider, but cases like this where you want to break the work up into blocks are a good match for the older batchable approach.
    – Keith C
    Jan 16, 2018 at 15:33

You must log in to answer this question.

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