1

Have any one of you come across a situation, where I need to call queueable class from batch. To give you more details, let us a scenario - I have a scheduler, which will run after particular time of interval; which will call a batch method withing its execute method. Lets say batch of Cases which needs to processed.

In batch for each entry, we need to have 1 callout + process data 1 callout + DML in sync.

We tried following thing:

Batch class:

lObject = List of object(which needs to be processed)

    for(Object o: lObject)
    {
        if( Limits.getQueueableJobs() <= 25) 
        {
            System.debug('in batch class  limits : ' + Limits.getQueueableJobs() );
            System.enqueueJob(new queueableClass(o));    
        }
        else break;
    }

Queueable Class:

  1. callout1
  2. processData
  3. Again enqueue- newEnqueueClass
  4. newEnqueueClass : Insert data
  5. future callout [since we cannot have callout after DML].

PS:For some reason, we cannot bulkify callout(due to 3rd party system limitation).

What should be ideal solution to this problem?

  • Do "1 callout + process data and DML" in batch, set a flag on processed records. After batch done processing in finish run another batch on marked records with "+ 1 callout in sync." – Denis Pereverzev Feb 2 '18 at 13:22
  • Have updated flow please check. We have dependency here. Like we need to process data quickly-like communication stuff(email and sms). – Ysr Shk Feb 2 '18 at 13:45
3

Assuming you can't refactor to allow things to happen for you, how about using this pattern:

public interface Action
{
    void execute();
}

public class CalloutPlusDMLAction implements Action
{
    private Id recordId;
    public CalloutPlusDMLAction(Id recordId)
    {
        this.recordId = recordId;
    }
    public void execute()
    {
        // make callout to 3rd party system
        // do your dml
    }
}

public class QueueableAction implements Action
{
    private Id recordId;
    public QueueableAction(Id recordId)
    {
        this.recordId = recordId;
    }
    public void execute()
    {
         System.enqueueJob(new YourQueueable(recordId));
    }
}

public class ActionBatch implements Database.Batchable<Object>, Database.AllowsCallouts
{
    public Iterable<Object> start( Database.BatchableContext bc )
    {
        List<Action> actions = new List<Action>();
        for(Case ocase : [SELECT Id FROM Case])
        {
            actions.add(new CalloutPlusDMLAction(oCase.Id));
            actions.add(new QueueableAction(oCase.Id));
        }
        return (Iterable<Object>)actions;
    }
    public void execute(Database.BatchableContext BC, List<Object> scope) 
    {
        for(Action action : (List<Action>)scope)
        {
            action.execute();
        }
    }
    public void finish(Database.BatchableContext bc){}
}

Then, set your batch size to 1 and call as follows:

Database.executeBatch( new ActionBatch(), 1 );

For each iteration of execute() you'll get the web service callout and DML happening first, followed by the queueable in the next iteration. Or perhaps you won't even need your queueable by separating each execution context using batch size of 1.

  • will check and update you soon. Thanks for your efforts! – Ysr Shk Feb 2 '18 at 13:51
  • Thank you alot..!! It worked great!! However, I did not understand, how using these lines(along with the interfaces and single queueable interface) makes this thing to work). Could you please explain what went wrong in my case(whenever you got some time). Line: actions.add(new CalloutPlusDMLAction(case.Id)); actions.add(new QueueableAction(case.Id)); – Ysr Shk Feb 2 '18 at 15:51

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.