0

I'm working on creating a batch which inserts a child record to accounts. The child record creation kicks off a separate process that relies on a lot of specific information being in place and is prone to failures, which we're trying to log on the accounts if the child record fails when its inserted. The partial success from AllOrNone being false doesn't seem to be working as expected, and when running more than a single record, if any fail at all the entire batch is updated to reflect the error. Example of the batch below (updated to be a bit more generic). Am is using the saveresult incorrectly or is there some other issue here? It works correctly when running 1 at a time.

global class Batch_CreateDunningInstance implements Database.Batchable<sObject>, Database.AllowsCallouts, Schedulable {

public Iterable<sObject> start(Database.BatchableContext bc) {
    system.debug('start of Batch_CreateDunningInstance');
    
    List<Account> scope = [SELECT Id, MS_Dunning_Profile__c
                           FROM Account
                           WHERE Create_Dunning_Cycle__c = true and Dunning_Criteria__c = true];
    return scope;
}

global void execute(Database.BatchableContext BC, List<Account> scope) {
    system.debug('entered execute of Batch_CreateDunningInstance');
    List<Dunning_Instance__c> dunningInstancesToInsert = new List<Dunning_Instance__c>();
    Map<Id, Account> accountsToUpdate = new Map<Id, Account>();
    List<Account> accountsToUpdateList = new List<Account>();
    
    for (Account acct : scope) {
        Dunning_Instance__c di = new Dunning_Instance__C(
            Account__c = acct.Id,
            Dunning_Profile__c = acct.MS_Dunning_Profile__c
        );
        dunningInstancesToInsert.add(di);
        accountsToUpdate.put(acct.Id, acct);
    }
    
    Database.SaveResult[] insertResults = Database.insert(dunningInstancesToInsert, false);
    
    for (Integer i = 0; i < insertResults.size(); i++) {
        Account acctToUpdate = accountsToUpdate.get(dunningInstancesToInsert[i].Account__c);
        
        if (insertResults.get(i).isSuccess()) {
            acctToUpdate.Create_Dunning_Cycle__c = false;
        } else {
            acctToUpdate.Dunning_Errors__c = insertResults[i].getErrors()[0].getMessage().trim();
        }
        accountsToUpdateList.add(acctToUpdate);
    }
    
    if(!accountsToUpdateList.isEmpty()) {
        update accountsToUpdateList;
    }
    
}

global void finish(Database.BatchableContext BC) {
}

global void execute(SchedulableContext sc) {
    Database.executeBatch(new Batch_CreateDunningInstance(), 20);
}

}

7
  • 1
    What exactly do you mean by "the entire batch is updated to reflect the error"? What error do you get? The code looks generally okay to me. You are probably aware your Account update will be all-or-nothing and want to take the risk. An alternative would be to rollback only, if not even Accounts with new kids could be updated. In any case I'd be careful that updates of the two Account fields can hardly fail (i.e. the Accounts are generally in a state to be updateable) - and to avoid further complications, the insert of kids should not trigger Account updates on their own... Commented Sep 5 at 11:28
  • If i have the batch set to run for 2 particular records in the same execution, one that i know should insert successfully, and one that i know will fail, when the batch runs it will not insert either child record and will update both of the accounts to have the error from the singular failed record.
    – Logie C
    Commented Sep 5 at 14:16
  • 1
    That suggests the update to the accounts fails, aborting the whole transaction in the execute method. BTW, your start method is poorly written; you should return a Database.QueryLocator by simply returning the static or dynamic query wrapped in Database.getQueryLocator. As it stands you actually execute the query, so this won't work with > 50K Accounts.
    – Phil W
    Commented Sep 5 at 14:28
  • If the account update was failing, it wouldn't get its errors__c set though would it? Also I get the concern on the start but this shouldn't ever have more than about 1k accounts in scope so that should not be an issue.
    – Logie C
    Commented Sep 5 at 14:39
  • 2
    The observed bug ("update both of the accounts to have the error from the singular failed record") can't be explained based on the presented code. Would it be possible you unknowingly removed the erroneous bits by simplifying the code for presentation purposes? Commented Sep 5 at 15:57

1 Answer 1

-1

I had an incorrect assumption that AllOrNone would allow some records to process and others to fail while retaining successes throughout the transaction. If any fail the entire transaction fails/rolls back and the save result will display that regardless of the index.

1
  • 2
    Your initial assumption is true. Please have a look at the documentation and test. If some of the inserts fail, this does not prevent the others from succeeding - provided AllOrNone is false. Commented Sep 6 at 9:15

You must log in to answer this question.

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