0

I need to clone +10000 records. Yes,I'm aware of all of the limits and pitfalls associated with tying a trigger to a batch apex class. I'm accounting for it as only 1-2 people will be able to perform the action.

Some information on the process, on a new record creation of Object A that record is now active and all Object B child records of the previous Active Object A must be 'cloned' and reparented to the new "Active Object A Record". This is the list of records that I would hope to pass in some way to the batch class.

I have performed the above in an apex handler class's helper method and would love to insert them via a bulk apex job but have no idea how to do that.

I'm new to bulk apex. Please, any help anyone would have to walk me through this would be greatly appreciated.

Thanks

4
  • 1
    Welcome to SFSE!. Please see How to Ask on getting the most from the community. You'll want to pass in to the constructor of the batchable the Id(s) of the previously active Accounts - then the batchable start() can query for the Contacts
    – cropredy
    Jun 23, 2023 at 22:39
  • As a general note: A primary concern around async apex (@future, batch, queueable, schedulable) and triggers isn't really so much the number of people who can perform the operation. Rather, the concern is the number of times that async code can be called from a trigger (i.e. If you're making a separate call to async code for every individual record in a trigger, that's a problem). One of the key parts of coding on the Salesforce platform is going out of your way to do things (queries, dml) to collections (maps, sets, lists) of records rather than on individual records.
    – Derek F
    Jun 24, 2023 at 0:23
  • Also, "bulk apex" and "bulkification" are just shorthand for "make sure you don't have any queries or DML operations inside of a loop".
    – Derek F
    Jun 24, 2023 at 0:26
  • sorry it's been a long day of mixing up terms ^you're right I just meant batchable Jun 24, 2023 at 2:11

2 Answers 2

0

Just have the child records be the records to iterate:

public class Example implements Database.Batchable<ObjectB> {
  Id sourceId;
  Id targetId;
  public Example(ObjectA source) {
    ObjectA target = source.clone(false, false, false, false);
    insert target;
    targetId = target.Id;
    sourceId = source.Id;
  }
  public Database.QueryLocator start(Database.BatchableContext context) {
    return Database.getQueryLocator([
      SELECT ...
      FROM ObjectB
      WHERE Parent__c = :sourceId
    ]);
  }
  public void execute(Database.BatchableContext context, ObjectB[] scope) {
    ObjectB[] clones = scope.deepClone(false, false, false);
    for(ObjectB clone: clones) {
      clone.Parent__c = targetId;
    }
    insert clones;
  }
  public void finish(Database.BatchableContext context) {
  }
}

This will have a bit of work to do, but this should be a decent starting point.

3
  • but then how do we define / pass in our targetId/SourceId Values? to match the 'triggering /new record"... Also thank you this is definitely helpful Jun 24, 2023 at 2:10
  • @AndrewBloch Database.executeBatch(new Example(originalRecord));. I'm not sure how you plan on triggering this. If the user is creating a record, and you're kicking off the process in a trigger, then you'd actually probably want to start with public Example(ObjectA target) { targetId = target.Id; sourceId = target.Source__c; } where Source__c is the previous active record.
    – sfdcfox
    Jun 24, 2023 at 3:26
  • Given you want to clone 10000+ parents with (unspecified) number of children, be aware that using a batch of this form, an execution per parent, will use a minimum of 30000+ async calls, more if you have large numbers of child records to clone and do not have optimal chunk sizing. That said, if you don't do this often (no more than once a day) and have a lot more than this many async executions available per day after all other async processing, you should be OK.
    – Phil W
    Jun 24, 2023 at 11:28
0

I think the primary question is how the objects to be cloned are identified, and processing initiated, with the rest of this being far easier to then address after that is effectively solved. Given that you require large scale cloning, it's really important to address this in a bite sized manner, adhering to governor limits.

Personally, I'd mark each "parent" record that needs the cloning processed (I think I would leave the creation of the equivalent "parent" clone to be part of that same process) and then have an "asynchronous" process that iteratively does the required cloning. Importantly you'll need to carefully keep track of the records that can be cloned in a single transaction by tracking SOQL, DML and CPU limits. The key here is to only process as many of the given "parent" objects as you know you have SOQL and DML limits available for all their existing "children".

I'd do something like query n original "parent" records that have been marked for cloning and then use an aggregate count query to count the children for each, then take the first m "parents" where I know I have limits to complete their whole processing in the one transaction, leaving the rest to be processed in a subsequent iteration.

For me, a batch isn't the ideal fit since it processes a deterministic number of records in one go, but here you need to adapt the processing depending on the number of child records you are handling per parent.

I'm not posting a complete solution here, but have a look at this github repo that illustrates the general principles here (though not the limit tracking). You can also read the associated article I wrote on the subject.

NB: If any of your "parent" objects can have more than 9998* children then the above approach won't work (you would not be able to perform the cloning for that one parent with its children in a single transaction) and you would need to determine an alternative solution. However, Salesforce recommends against having more than 10000 children per parent, as covered in the Parent-Child Data Skew documentation.

*: 9998 allows for the original parent's "clone me" marking to be cleared, and for the clone parent to be created along with its children within the 10000 "total number of records processed as a result of DML statements" per transaction limit.

You must log in to answer this question.

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