8

I've been looking for similar questions, but none of them applies to my scenario, so I'd be grateful if you can explain me why my logic is failing, and what can I do in order to fix it.

  • I want to run every month a logic on Account records (>50k) : BATCHS

  • This logic will create X events on each account : Event Trigger

  • For new Events, I need to sync the data with other system : @future method

So, since Batchs cannot call another future methods, I decided to implement the @future logic inside a Queueable class.

Here is a snippet:

Batch

global class BATCH_GeneradorEventos implements Database.Batchable<sObject>
{
       ...
       global void execute(Database.BatchableContext BC, List<sObject> scope)
       {
       ...
          insert evs;
       }
       ...
}

Event trigger

ID jobID = System.enqueueJob(new EventQueueableJob(Trigger.newMap.keySet()));

Queuable class

public class EventQueueableJob implements Queueable, Database.AllowsCallouts
{
    ...
    public void execute(QueueableContext context) 
    {
      // future methods and logic
    }
}

The error that I'm getting is:

"Too many queueable jobs added to the queue: 2"

and it's giving me for every SerialBatch.

Is it because I'm doing an enqueueJob for every iteration of the Batch? If so, how can I fix/remodel it?

Thank you in advance!

3
  • Minor note, the plural of batch is batches. – Adrian Larson Feb 8 '18 at 13:02
  • What's the size of evs? If it's over 200, you will hit this error certainly. – Adrian Larson Feb 8 '18 at 13:04
  • @AdrianLarson yes, it was like that. If you want to write the comment as an answer, I'll mark it as Best Answer. Thanks! – user3529582 Feb 23 '18 at 7:54
6

You have two things going on here:

  1. You can only execute one job from a job.
  2. Triggers fire in chunks of 200 records.

So if you update 201 records, you will have one Queueable kicked off with 200 records, and another kicked off with just one record. As soon as you execute a second job, you're going to error out. The simple fix is to keep your batch size low enough that you know evs will contain fewer than 200 records. A more robust fix is to add some logic to check if you should execute synchronously or asynhronously, and only use Queueable in the latter case.

8

Salesforce says you can add 50 jobs to the queue per transaction. But what Salesforce doesn't mention clearly is that it is not applicable for batch, you can only enqueue one job. Even if you enqueue your job in the trigger, you are still in a batch context.

  • Make sure you don't call enqueueJob twice at any point in the same transaction. For example an insert that would call an update and would execute enqueueJob twice.
  • Make sure you don't have enqueueJob in a for loop.
  • Make sure your code is bulkify.
5
  • Thanks for replying! enqueueJob is only being called in the after insert scope of the trigger. Also, the logic in the Queueable doesn't modify the events, it launchs a WebService. My question is: every batch calling the enqueueJob is considered as same transaction, even if they are in different SerialBatchs? – user3529582 Feb 8 '18 at 10:30
  • We would need to see a bit more code to understand where is your problem. Try to turn on your debug log, and before system.enqueueJob, add System.debug('Enqueue Limit : '+Limits.getQueueableJobs()); – Cloud Ninja Feb 8 '18 at 10:36
  • Each execute batch is a separated transaction. – Cloud Ninja Feb 8 '18 at 10:37
  • Ok, so what if I create a Batch for inserting the Events (>800k), and instead of calling the Queueable from the trigger, I call it from the finish method within the Batchable class? If I understood you properly, and every execute is a separated transaction, so it will be every finish, right? Then, I'd be able to .enqueueJob from there. – user3529582 Feb 8 '18 at 11:16
  • How can I achieve this?:Make sure you don't call enqueueJob twice at any point in the same transaction. For example an insert that would call an update and would execute enqueueJob twice. – NBR Jun 10 at 13:33
3

Here's another use case one might encounter when getting this error

Given: Batch job execute() method that updates two records A and B

Given a Process Builder with two decision blocks / action group pairs that
execute upon changes to records A and B, respectively. 
That is decision block 1 applies to record A and decision block 2 applies to record B.

Given that the Process Builder action groups do either DML or call invocable apex.  
Underlying apex code then does a System.enqueueJob(...)

Although Process Builder is bulkified, it is not bulkified across action groups. Thus, even if the action groups invoke the same invocable method or DML the same SobjectType, you will end up with two separate System.enqueueJob calls.

Bottom line, code defensively before executing System.enqueueJob(..) as you might be surprised over time the context in which it is invoked. For example, in our use case, we had good unit tests that made sure that service X enqueued the job as expected; but no unit tests that checked to see if the service were called twice from a batchable context.

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.