2

This is a basic question instead of code related. I am writing a batch job which will update Application__c records. Trigger for Application__c object, enqueues a queueable job to update cases

Batch Job -> Update Application__c -> after update trigger -> queueableJob

A single chunk of batch job will cause 1 queueable job to be spawned from trigger.

From what i understand, each chunk of batch job is considered a separate transaction. So each junk gets that quota of 50 queuable job, am i correct? So i do not have to worry about exceeding the limit of queuable job per transaction? Becuase each junk calls trigger only once, which spawns only 1 queuable job

or does queueable jobs spawned by all the chunks add up to that 50 job limit?

I was pretty sure it's the former scenario (where i dont have to worry about limit). But reading this confused me - https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_queueing_jobs.htm

Under the section of "Queueable Apex Limits", second bullet point says -

You can add up to 50 jobs to the queue with System.enqueueJob in a single transaction. In asynchronous transactions (for example, from a batch Apex job), you can add only one job to the queue with System.enqueueJob

I assume each chunk of batch job is considered "single transaction" on its own. Reading "Cloud Ninja" response on this post - Error "Too many queueable jobs added to the queue: 2" where he says it is not applicable for batch, you can only enqueue one job threw me off.

So which one is it? Queueable jobs added up together for all the chunks is subject to that 50 job limit? Or queueable jobs spawned by EACH chunk is subject to that 50 job limit?

2 Answers 2

5

A DML statement is a "sub-transaction" within the larger "transaction," which can be from Batchable, Schedulable, future, RemoteAction, AuraEnabled, etc. I like to use the analogy of a log file. One debug log file is one transaction, full stop. If you execute your batchable class and no results are returned, there are two logs, one each from start() and finish(). If execute() is called, you'll see another log for every call to execute(), each being one chunk of data returned from start().

Future, Batchable, Queueable, and Schedulable are all asynchronous code. They have relaxed permissions on memory and CPU time, but restrictions on how many additional asynchronous methods they can spawn. This is to avoid the "rabbit virus" effect, where one job spawns two, each of those spawn a total of four, eight, 16, 32, etc.

You get one asynchronous call for the entire execute() method. If you have it in a trigger, you'd better be ready to commit at most 200 records per transaction, or come up with an alternative approach.

How I handle this is, in my trigger, I check if I'm in synchronous or asynchronous code. If synchronous, I fire off my asynchronous code directly. If I'm already asynchronous, I instead call the code directly, since I know I have additional CPU and memory to work with.

I've also addressed this as a separate answer, it would take a slight modification, but you could "buffer" your queueable calls and hold them to the end, then fire it off when you're done. There's some complications if you worry about partial retries, etc, but it'd certainly be a place to start.

The general idea is that you'll need to consider if you're asynchronous or not, and deal with the reduced asynchronous limits if you are.

2

Each batchable execute() is a single transaction

Thus:

  • The batchable execute(), which you are calling a "chunk" can enqueue a job but that job may enqueue no others
  • The 50 enqueued jobs per transaction does not apply in your use case because the batchable execute() is a single transaction

You must log in to answer this question.

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