4

When my CustomObject1 is inserted or updated, I need to update a field on the Account object potentially traversing all the CustomObject1 who have a lookup-relationship to given accounts.

In principle, this was done using in-transaction update, but this quickly resulted in a violation of the limits, so the approach I took was to use QueueableApex. When a transaction of CustomObject1 insert or update occurs: - I collect all the Accounts who are impacted - I perform a count query for CustomObject1 instances on accounts.

Then I queue separately the jobs for accounts with many CustomObject1 (one job for account) from the one that have less than 5 CustomObject1 per account (single job). However this approach doesn't work, for example, when there are other jobs scheduled or other transaction on the same objects executed within short time interval.

I imagine a better approach would be to use all the limits in the current job and then when close to the limits queue a new one, but it is not obvious to me how to implement such a pattern.

What are the queueable design patterns available?

3
  • Do you have something to achieve like rollup summary in master-detail? Commented Mar 14, 2020 at 12:05
  • It is a lookup, I am trying to achieve something similar but obviously it cannot be executed in a trigger
    – Edmondo
    Commented Mar 14, 2020 at 12:05
  • If similar as rollup summary from lookup relationship type, there is a package available which might work for you, github.com/afawcett/declarative-lookup-rollup-summaries or may be you want to describe what exactly you are trying to achieve..! Commented Mar 14, 2020 at 12:23

1 Answer 1

7

The "too many queueable jobs" error is a per transaction error. You can have an "unlimited" number of queueable and future items in the queue of jobs to execute, unlike scheduled and batchable jobs. The only rule is you can only enqueue 50 at once, or 1 when you're already in asynchronous context. You need to change your Queueable design so that you only add as few jobs as necessary to get your business logic done. Also remember that a Queueable job can "chain" itself, so a better design is to use one job, start executing your logic, and when you reach a certain threshold of governor limits, chain to another job.

public void execute(QueueableContext context) {
  while(freeLimitsAvailable() && !recordsToProcess.isEmpty()) {
    proessMore(recordsToProcess);
  }
  if(!recordsToProcess.isEmpty()) { // Still more work to do... //
    System.enqueueJob(this);
  }
}
Boolean freeLimitsLeft() {
  return Limits.getDmlRows()/(Decimal)Limits.getLimitDmlRows() < 0.8 &&
         Limits.getDmlStatements()/(Decimal)LImits.getLimitDmlStatements() < 0.8 &&
         ...; // check all the limits you care about... //
2
  • thanks, but I see you are setting a 0.8 limit, but that's what I wanted to avoid. Isn't that arbitrary or dangerous?
    – Edmondo
    Commented Mar 23, 2020 at 11:52
  • @Edmondo My apologies for missing this comment. Yes, this implementation is more descriptive than prescriptive. Ideally, an org would have either a canary test (a test that fails a deployment when the limits get close enough) or a transaction finalizer that adjusts the limits based on past observations of execution limits. My main concern was simply that one should minimize the number of queuables per transaction to avoid the specific problem of using too many async jobs in one transaction.
    – sfdcfox
    Commented Sep 1, 2023 at 19:05

You must log in to answer this question.

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