+ Start a Discussion
Natalya Murphy 8Natalya Murphy 8 

How to bulkify call to Flow from Apex

I have a requirement to call Flow 2 after all transaction processing from Flow 1 has been completed and to avoid creating duplicate records in Flow 2.   The issue is that I need all records, including cascading record updates, to all be fully finished before I call Flow 2. 

I'm using an Apex Action to watch the transaction processing and determine when it's safe to call Flow 2, and also to prevent duplicate records from getting sent for processing.  But the way I have the Apex code written right now, all invoked flows are getting started sequentially rather than in parallel, and therefore the queries in the flows aren't bulkified.  This causes me to hit the dreaded Too Many SOQL Queries limit.

How do I invoke multiple flows in parallel so that their SOQL operations get bulkified?   I haven't found any documentation online that answers this.

Details of the required automation below, and code snippet follows after.

Automation Requirement
Flow 1 is an automation that runs when a Campaign Member is updated.  This Flow, in some scenarios, may update or create other Campaign Member records (which triggers a cascading Process Builder).

Flow 2 is an automation that runs and sums up the Campaign Member status at the Account level.  It creates/updates records in a junction object called Account Campaign Status having the following fields:
- Account (lookup)
- Campaign (lookup)
- Overall status (text field)  - logic-derived status based on the "highest ranking" status of any contact in this account that belongs to the given Campaign

If I try to call Flow 2 directly from Flow 1, I run into 1 of 2 problems:
- If a record already exists in Account Campaign Status, I hit a governor limit if more than 12 Contacts from the same Account are in the Campaign.  The limit is around preventing more than 12 concurrent updates.
If a record doesn't already exist in Account Campaign Status, and I have multiple Contacts from the same Account, I get multiple new rows created instead of just 1 row.  I think this is because all the flows are running in parallel and therefore none of them find a record in the system at first, and then all of them try to therefore create a record.  So instead of 1 record getting created, I get 12 if there are 12 separate contacts.

Below is the transaction control code I created to compile all the account/campaign combinations and, when all upstream transactions have finished, call Flow 2. 
public class AccountCampaignRollupAction {
 static Map<ID, Set<ID>> toCalculate = new Map<ID, Set<ID>>();    
    @InvocableMethod(label='Launch Account Campaign Rollup' description='If all transactions are done processing, calls an invokable flow to roll up campaign status at the account level')   
    public static void start( List<AccountCampaignRollupRequest> requests ){
        Set<ID> nextSet;
        //go through the list of requests and pull out the unique combinations of account/campaign ID 
        //for later processing
        for( AccountCampaignRollupRequest nextReq : requests ){
            nextSet = toCalculate.get( nextReq.accountId );
            if( nextSet == null ){
                nextSet = new Set<ID>();
            }
            system.debug('Acct: ' + nextReq.accountId + ', Campaign: ' + nextReq.masterCampaignId);
            nextSet.add( nextReq.masterCampaignId );
            toCalculate.put( nextReq.accountId, nextSet );
        }//for
        
        Map<String,Object> flowVars = new Map<String,Object>();
        Flow.Interview myFlow;
        
        //now that we've built up our unique list, see if we're done with all transactions.
        //If we're not done, don't do any processing yet.
        //The static toCalculate variable will keep compiling more IDs for processing.
        if( TransactionControl.activeTransactionCount() < 1 ){
            //all transactions finished.  It's safe to kick off the calculations.
            for( ID nextAcct : toCalculate.keySet() ){
                nextSet = toCalculate.get( nextAcct );
                for( ID nextCampaign : nextSet){
                    flowVars.clear();
                    flowVars.put('InputAccountId', nextAcct );
                    flowVars.put('InputMasterCampaignId', nextCampaign);
                    system.debug('Calling flow with map: ' + flowVars);
              //HERE'S THE PROBLEM SECTION.   Because this flow is invoked in a for-loop, the flows
              //launch sequentially and their queries aren't getting bulkified.  
              //Is it possible to make a bulkified call to launch multiple flows?
              //Or if it's not possible, how do I pass in a list of account-campaign pairs
              //and then query them from the flow?
                    myFlow = Flow.Interview.createInterview( 'Account_Campaign_Status_Rollup', flowVars );
                    myFlow.start();
                }//inner for               
            }//outer for          
        }//if
        else {
            system.debug( 'Transactions still running.  Not calling flow yet' );
        }
    } 
    
    public class AccountCampaignRollupRequest{
        @InvocableVariable(required=true)
        public ID accountId;
        
        @InvocableVariable(required=true)
        public ID masterCampaignId;
    }
}

 
Rohit TantiRohit Tanti
Hindi status newstatus.in (https://newstatus.in/sad-status-hindi/)