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

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) {
    }
}
  • 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){...}). – Maksim Teslenko Jan 16 '18 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 '18 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.

  • Fully agree with you Akram G, but unfortunately this is a clients API. Thank you for your input. – Maksim Teslenko Jan 16 '18 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 '18 at 15:33

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.