2

So, I have this method:

@AuraEnabled
public static string batchChangeItemStatus(Map<String, Map<String, Object>> filters, String status){
    List<Item__c> items = getSearchedItems(filters);
    System.debug(items);
    List<String> sellerIds = getSellerIds(items);
    System.debug('sellerIdBatch ====>' + sellerIds[0]);
    BatchApexListItems batch = new BatchApexListItems(getCsvItems(items, status), sellerIds);
    return Database.executeBatch(batch);
}

As it is, you can see that I am calling it only one time, passing as parameter a list of CsvItems (a custom apex class) and a list of sellerIds which are strings. The thing is, the batch class actually can't process more than one seller (despite accepting a list) so I have to separate the items corresponding to each sellerId and call the batch one time for each Id. Is it possible to call the batch several times one after the other, like for example in a for loop? would it run correctly, with no problems, or do I have to process it in a certain way?

4
  • You could easily bump into flex queue limits; an org allows up to 5 concurrently executing batches and then up to a further 100 in the flex queue. However, once the flex queue is full every attempt to execute another batch will bomb out with a limits violation. Remember this is a shared limit for all batches on your org. I think you would probably be better off changing how the async processing is implemented in order to maintain robustness.
    – Phil W
    Mar 29, 2021 at 13:58
  • As I have seen in different production orgs we manage, there are maximum 4/5 sellers, so there wouldn't be more than that number of batch calls. If I can guarantee that, is it ok to do it like this? Mar 29, 2021 at 14:01
  • As long as you fit within the apex limits you can do it this way. Still, it may be a fragile solution - what if you get more instances of the batch queued whilst others are still running against the same seller(s), for example? They could (in principle) run concurrently and you might get some form of data "race condition".
    – Phil W
    Mar 29, 2021 at 14:04
  • Yes, I understood it may be a fragile solution, so I searched the web and couldn't find a similar situation. This is why I asked here to see if there is a best practice regarding multiple batch apex calls, as I'm relatively new to all this. Thanks for the help! Mar 29, 2021 at 14:10

1 Answer 1

4

As folks in the comments have said, you're likely to hit limit issues if the volume is sufficiently high. There are two solutions I've used for this. The first solution sounds close to what you're trying to do, so I'll present it first. The second I've found to be more reliable, but it may take some refactoring on your part.

First solution is to use a Queueable instead of a batch job. Queueables allow you to pass arbitrary data from one asynchronous job to another, as opposed to a flat list of records. So what you can do is create a Queueable that holds everything that you want to process, and only do as much as you want each time it runs.

@AuraEnabled
public static string batchChangeItemStatus(Map<String, Map<String, Object>> filters, String status){
    List<Item__c> items = getSearchedItems(filters);
   System.debug(items);
    List<String> sellerIds = getSellerIds(items);
    System.debug('sellerIdBatch ====>' + sellerIds[0]);
    QueueableApexListItems q = new QueueableApexListItems(getCsvItems(items, status), sellerIds);
    return System.enqueueJob(q);
}

public class QueueableApexListItems implements Queueable {
    public List<Item__c> csvItems  { get; private set; }
    public List<String>  sellerIds { get; private set; }
    public Integer       sellerIdx { get; private set; }
    public Integer       itemIdx   { get; private set; }

    public QueueableApexListItems(List<Item__c> csvItems, List<String> sellerIds) {
        this.csvItems  = csvItems;
        this.sellerIds = sellerIds;
        this.sellerIdx = 0;
        this.itemIdx   = 0;
    }
    
    public void execute(QueueableContext x) {
        if (this.sellerIdx == this.sellerIds.size() || csvItems.isEmpty()) return;
        Id sellerIdBatch = sellerIds[this.sellerIdx];
        
        // process end of a seller
        if (this.itemIdx == this.csvItems.size()) {
            // do any final processing
            this.itemIdx = 0;
            this.sellerIdx++;
            System.enqueueJob(this);
        }
        
        // process item for seller
        else {
            Item__c csvItem = csvItems[this.itemIdx];
            // perform processing
            this.itemIdx++;
            System.enqueueJob(this);
        }
    }
}

The queueable allows you to re-enqueue itself from within its own run. You are able to enqueue exactly one queueable from inside the running context of a queueable, so this allows you to create a chain of asynchronous processes that run one after another. Since you're talking about multiple batch jobs, which themselves would contain multiple records to process, my working assumption in the code above is that you're trying to "loop through" each seller for each item. So I demonstrated how to manage that complexity in a single queueable using two arrays, and two indices.

The second option would be to use platform events. Each platform event would be given a seller id and an item id, and then you'd write a trigger handler to process the event, querying any data needed afresh. Our org has moved more toward this strategy, because we've found platform events to be much more graceful in failure. When a failure occurs (e.g. due to a transient record locking error or whatever), you can catch the error and replay platform events, rather than have it fail silently. Also, if one event fails, it won't necessarily take down every other record combination you're trying to process. Queueables are problematic in that one failure can take down the whole queueable, which will stop everything that was loaded in the queueable to process. We of course catch all our errors, but our org has problems with CPU timeouts, which cannot be caught, so we've moved more toward platform events.

Hope that helps!

2
  • 1
    thank you! I'll bring it up next time I talk to my coworker, and make a decision. It really helps! Mar 30, 2021 at 17:10
  • Great, glad to help! Mar 30, 2021 at 17:40

You must log in to answer this question.

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