3

I am using a queueable class to handle various date ranges that are then sent to a web service.

Upon calling the queueable class I am receiving the following error:

Callout not allowed from this future method. Please enable callout by annotating the future method. eg: @Future(callout=true).

When I try to add the @future method I receive the following error:

Future methods must be declared as static

I then add static and receive the following error:

Future methods do not support parameter type of System.QueueableContext

Running the following in Execute Anonymous Window to test:

Datetime now = Datetime.now();
integer dayIter = 20; 
for(integer i = 0; i < dayIter; i++)
{
     datetime endtimedatetime = now.addDays(-3*(i-1)); 
     string endtime = endtimedatetime.formatGmt('yyyy-MM-dd HH:mm:ss.SSS\'Z\'');
     datetime starttimedatetime = now.addDays(-3*i);
     string starttime = starttimedatetime.formatGmt('yyyy-MM-dd HH:mm:ss.SSS\'Z\'');
     system.debug('starttime: ' + starttime);
     System.enqueueJob(new getAccountUpdatesfromEndpoint(starttime, endtime));
}

getAccountUpdatesfromEndpoint Class:

public class getAccountUpdatesfromEndpoint implements queueable {

public string receiveStartTime;
public string receiveEndTime;

public getAccountUpdatesfromEndpoint(string startTime, string endTime) {
    receiveStartTime = startTime;
    receiveEndTime = endTime;
    }

public void execute (Queueable Context context){

string jsonstr = '';
List<Custom_Object__c> accList = new list<Custom_Object__c>();

    String starttime = receiveStartTime;
    String endtime = receiveEndTime;

     HttpRequest request = new HttpRequest();
     .....

     try {
         .....
         jsonStr = res.getbody();             
         JSONConvertEndpoint endpointGet = (JSONConvertEndPoint)JSON.deserialize(jsonStr, JSONConvertEndPoint.class);

         for(integer i=0; i < endPointGet.size(); i++) {
             Custom_Object__c acc = new Custom_Object__c();
             acc.Field__c = endPointGet[i].field;
             accList.add(acc);
         }

         upsert accList;


     }

   catch {
          /* exception logging */
         }


}

Here is the link to my previous stack question: Schedule Iteration of Webservice Callouts

2

I would build an object and pass it into the queueable something like

public class AccountUpdateDates
{
    public AccountUpdateDates(){}
    public String startDate {get;set;}
    public String endDate {get;set;}
}

Then for for your dates (how ever you do it, going to use your for loop logic)

Map<Integer, AccountUpdateDates> accountUpdatesDates = new Map<Integer, AccountUpdateDates>();
for(Integer i = 0; i < 20; i++)
{
    AccountUpdateDates aud = new AccountUpdateDates();

    Datetime endtimedatetime = now.addDays(-3*(monthIter-1)); 
    String endtime = endtimedatetime.formatGmt('yyyy-MM-dd HH:mm:ss.SSS\'Z\'');
    aud.endTime = endtime;

    Datetime starttimedatetime = now.addMonths(-3*i);
    String starttime = starttimedatetime.formatGmt('yyyy-MM-dd HH:mm:ss.SSS\'Z\'');
    aud.startDate = startDate;

    accountUpdatesDates.put(i, aud);
}

Then pass that entire object to your queueable, before you were constantly queueing jobs which kind of ruins the purpose of the queue

System.enqueueJob(new getAccountUpdatesfromEndpoint(accountUpdatesDates));

You will need to update your queueable to allow Callouts

implements Queueable, Database.AllowsCallouts

You can only enqueue up to 50 jobs at a time, you're doing 20 right now so you'd be okay but I still don't recommend doing it. Here is where it gets a little tricky, because you will need to figure out how to remove the dates from the Map<Integer, AccountUpdateDates> as you do you service call out (I believe you can use the Integer key here I was just spit balling). If you don't do this you will constantly be doing call outs on the first one in the Map, also I recommend you create a "kill switch" in custom settings in order to break out of this because there is a risk that you will get stuck in a constant loop of job queueing

Then at the end of this you will need check something like

if(!accountUpdatesDates.isEmpty())
{
   System.enqueueJob(new getAccountUpdatesfromEndpoint(accountUpdatesDates));
}

Remember you are removing the key/value pair after you do the call out. Also I would break this out into 3 different files the service, queueable, and the handler that calls the queueable but that's a bigger/different discussion

  • If I understand this correctly. and I need to test it still, but I believe I'd run into a separate heap issue error with the approach of using an object with all of the dates into one job. Regardless, this has resolved my current problem and the initial question with some great content to consider moving forward to build what might be a better solution. – S.B. Apr 27 '18 at 21:42
  • Asyncronous jobs have an increased heap size. I don't know why you would hit a heap size limit, also if you don't pass in the entire object to the queueable there is no reason to even use the queueable. Having a Map<Integer, Object> with 20 objects in there won't cause a heap issue. Why do you have to do such a large range of dates ? – EricSSH Apr 27 '18 at 22:16
  • To my understanding, the data from the endpoint is consistently being added to/updated so we have to go back x years to get the all of the updates. In my example, we are only updating one field, for the actual use case I'm updating 8 fields and the response from the endpoint includes anywhere from 30-100 records per day. The heap issue comes in when it tries to process this json response. My initial problem was reaching out to the endpoint from x year to y year and receiving a body too large for SF to handle. – S.B. Apr 27 '18 at 22:42
5

You need to add Database.AllowsCallouts to your class, not @future(callout=true). The error is erroneous.

public class getAccountUpdatesfromEndpoint implements queueable, Database.AllowsCallouts {

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.