1

I came across the information regarding the execution order of triggers during the merging of Salesforce accounts, as discussed in this SF Stack Exchange thread: merge-trigger-execution-order.

The standard behavior, as outlined in the provided link, indicates that when merging two accounts, the sequence of trigger events involves before delete, after delete, and before update for the winning record.

This process functions seamlessly when dealing with a single pair of accounts, for example:

enter image description here

However, my inquiry pertains to situations where a third-party tool, such as RingLead, allows the selection of multiple pairs of accounts for merging. In this scenario, each pair triggers the entire sequence (before delete, after delete, before update) three times (as in the example below) within the same transaction, even though the trigger.new size remains one for each pair.

enter image description here

To address this, I've considered using a static variable Set<Id> in my trigger handler to collect the winning records of each account. This process occurs within the same transaction, and in my set I get three values: [A,C,E]

My questions are as follows:

How is it possible that these operations happen within the same transaction? Given that the standard service is called, how does Salesforce recognize and manage this, and is it guaranteed to occur within a single transaction? for example, I thought that the Trigger.new would be three and not one, considering it was a bulk.

I looked at the Merge service here: https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_merge.htm

The second question:

Given that trigger.new.size() is consistently one, how can I determine the conclusion of the requests? I intend to perform manipulations on a set of winning accounts, such as creating two batches to handle related opportunities and subsequently updating related custom objects based on the winning accounts. This is particularly relevant while dealing with a large volume of records for both opportunities and custom objects. I aim to avoid processing a batch for each individual account.

Any explanation on this will be very appreciated.

Thank you!

EDITED Based on the SFDCFOX answer:

I attempted to create a platform event (publish after commit) to dispatch all the requests after the transaction concluded.

In my account Trigger - after delete (where the winning records are known), I publish the event via the EventBus.

    public static void MergeAccount(List<Account> newAccounts, Map<Id, Account> newAccountsMap, Map<Id, Account> oldAccountsMap) {
        for(Account a : oldAccountsMap.values())
        {
            System.debug('masterRecordId ::  ' + a.MasterRecordId);
            if(a.MasterRecordId!=null){
                List<Test_2__e> testEventList = new List<Test_2__e>();
                Test_2__e testEvent = new Test_2__e();
                testEvent.AccountIds__c = a.MasterRecordId;
                testEventList.add(testEvent);
                List<Database.SaveResult> results = EventBus.publish(testEventList);

                for (Database.SaveResult sr : results) {
                    if (sr.isSuccess()) {
                        System.debug('Successfully published event.');
                    } else {
                        for(Database.Error err : sr.getErrors()) {
                            System.debug('Error returned: ' +
                                    err.getStatusCode() +
                                    ' - ' +
                                    err.getMessage());
                        }
                    }
                }
            }
        }

    }

What I observed when sending three accounts in bulk from the third service is that all of them happen in one transaction, but the platform event occurs three times, once for each account.

If I try to do the same in a before update trigger, for example, when inserting a bulk of accounts and publishing the same EventBus when handling it, without any relation to the merge, it does work, and the platform event trigger gets all the account records in the same transaction at the end of the transaction.

Maybe if the merge API is used differently, could it affect the platform event in such a case?

Is there something I missed or didn't understand here?

1 Answer 1

1

How is it possible that these operations happen within the same transaction?

A transaction starts when an API call is made, and ends when the API call returns the final results and writes the debug log, if logging is enabled. As I like to say, one debug log is one transaction. Some calls, like merge, are treated as separate mini-transactions within the larger transaction. For synchronous API calls, it is guaranteed that the time between when an API method is called and its return value is one transaction. Asynchronous methods may result in multiple transactions. For example, a Batchable class will generate at least two logs (start and finish), and those are two separate transactions.

Given that the standard service is called, how does Salesforce recognize and manage this, and is it guaranteed to occur within a single transaction?

RingLead, and others, call the merge function with up to 200 MergeRequest objects, each having one ID to merge to, and up to 2 IDs to merge from. In code, it may look something like merge(requests), where requests is an Array/List, whatever is relevant for the language used. As I stated earlier, a transaction starts when a method is called, and ends when a return value comes back.

for example, I thought that the Trigger.new would be three and not one, considering it was a bulk.

That's true for most API calls, but not merge. This is likely because of the potential costs in resources of merging, as well as smoothing out some of the oddities that might otherwise occur. For example, if you want to merge 10 accounts into a single account, you can do this with 5 MergeRequest objects, and it won't cause complications. There's a few other API calls that behave this way as well.

Given that trigger.new.size() is consistently one, how can I determine the conclusion of the requests?

You can't do so from within the transaction. It's simply not available to us. We would need some kind of method to call or property to check to see if we're done, and there's nothing like that. That's one reason why you should generally consider writing your code as if it were running in isolation.

The solution to your problem is to use a Platform Event, and write a trigger for it. When you use the Publish on commit mode, the system will aggregate all of the events from a transaction together, and dispatch them in a new transaction that will be handled by the Platform Event trigger. This way, you don't have to collect the ID values individually to deal with them inside the transaction.

1
  • Hi @sfdcfox, many thanks for your time and fully detailed answer, and sorry for the delay. I tried to use a platform event (since writing my own code for merge is not relevant here), but I came across some issues. Since there isn't much space to write it here in the comment (and it's not organized like writing a question or answer), I edited it in my question. I'll be much appreciative if you can take a look at it. Thank you!
    – Salvation
    Jan 7 at 10:39

You must log in to answer this question.

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