12

I have a process built with a Process Builder. It fires on contact update/create.

Under certain conditions that process launches a Flow. Flow accepts two Parameters - ContactId and AccountId.

If I do mass update/insert of the contacts and flow is launched through the process, the flow is automatically bulkified disregarding the fact that flow itself accepts a single value of each Id. So, if I upload 100 contacts the flow will query info about the related accounts in one query.

Is there a way to implement the same behavior while launching the Flow from the apex?

I was trying something like the code below but it just launched flow 4 times and even put it into the same apex transaction.

Map<String, String> allParams = new Map<String, String>();
allParams.put('003XXXXXXXXXXX1', '001XXXXXXXXXXX1');
allParams.put('003XXXXXXXXXXX2', '001XXXXXXXXXXX2');
allParams.put('003XXXXXXXXXXX3', '001XXXXXXXXXXX3');
allParams.put('003XXXXXXXXXXX4', '001XXXXXXXXXXX4');

for(String contactId : allParams.keySet()){
    Map<String, Object> params = new Map<String, Object>();
    params.put('ContactId', contactId);
    params.put('AccountId', allParams.get(contactId));
    Flow.Interview myFlow = Flow.Interview.createInterview('GreatestFlowOfAllTimes', params));
    myFlow.start();
}

I also tried to pass list of parameters instead of single values like:

Map<String, Object> params = new Map<String, Object>();
            params.put('ContactId', new List<Id>{003XXXXXXXXXXX1, 003XXXXXXXXXXX2, 003XXXXXXXXXXX3, 003XXXXXXXXXXX4});
            params.put('AccountId', new List<Id>{001XXXXXXXXXXX1, 001XXXXXXXXXXX2, 001XXXXXXXXXXX3, 001XXXXXXXXXXX4});
            Flow.Interview myFlow = Flow.Interview.createInterview('GreatestFlowOfAllTimes', params));
            myFlow.start();

And it doesn't work as well.

Documentation does not provide info about bulkification at all.

Maybe I'm missing something. Please advise.

3 Answers 3

9

Winter '23 enables Apex to bulk invoke Flows - many interviews, one transaction.

The capability comes with the Invocable.Action classes. The invoke method can mass run any invocable step or indeed entire flows. Not to be confused with Flow.Interview.start which only supports a single interview. This uses Flow's bulk engine and doesn't involve collection variable workarounds.

The flow is bulkified automatically. It can be designed from the perspective of a single request.

Here are the docs for the Invocable.Action classes and methods: developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_Invocable_Action

Bulk flow invoke - list of maps style

// use the flow name
Invocable.Action impl;
impl = Invocable.Action.createCustomAction('flow', 'Create_Expense');

// prepare a collection of interviews
impl.setInvocations(new List<Map<String,Object>>{
    new Map<String,Object>{'amount' => 1.11}, // 1st interview
    new Map<String,Object>{'amount' => 2.22}, // 2nd interview
    new Map<String,Object>{'amount' => 3.33}  // 3rd interview
});

impl.invoke(); // start all interviews
Limits.getDmlStatements(); // many interviews, only 1 insert

Create_Expense example flow

Demo expense records x 3

Alternatively: Bulk flow invoke - fluent builder style

// create the flow by name, then chain the invocations
Invocable.Action.createCustomAction('flow', 'Create_Expense')
.setInvocationParameter('amount', 1.11).addInvocation() // 1st interview
.setInvocationParameter('amount', 2.22).addInvocation() // 2nd interview
.setInvocationParameter('amount', 3.33).invoke(); // 3rd interview and start

Limits.getDmlStatements(); // many interviews, only 1 insert
2
  • 3
    +1 for adding this ---I did not know this and it is a terrific addition to the Platform
    – cropredy
    Commented Jan 2 at 18:24
  • @cropredy your Record-Trigger tip is very innovative and saved many a day - cheers! Commented Jan 2 at 20:52
5

Calling start() on a flow interview object within a loop is NOT bulkified - even if all the flow interview objects are for the same flow.

This has to be because the start() method executes synchronously

So, when does this matter?

  • If the flow you are calling does
    • Get Record (SOQL - subject to 101 per transaction (sync)) or
    • any of the DML elements - Create/Update/delete - subject to total transaction DML limits

There's a reason why the Apex Flow.Interview documentation is slanted towards apex controller use cases - where you are likely to be operating on a single record and calling start() only once.

Now, can you work around this? Yes - but at a cost

If you design the called Flow to accept as input a collection of objects and to return a collection of results, then you only need the Apex code to marshal all the inputs into a single collection, invoke start() once, and then unmarshal the results once the Flow has returned.

You can see a trivial example of this in the Record Updater from the AndyInTheCloud Calling Flow From Apex blog post

The cost? Depending on your use case, if the flow does any database interaction, since the single Flow Interview is going to iterate over a collection of inputs, it has to build collections of variables in loops so a single SOQL or DML is performed per Flow Interview. You might as well code up the logic in Apex rather than try to replicate the conciseness of Apex control logic in Flow Loop, Assignment, and Decision elements.

Other references

1

Here is some documentation about flow bulkifkcation.

https://help.salesforce.com/articleView?id=flow_concepts_bulkification.htm&type=5

My understanding is that Salesforce will "intelligently" bulkify the query to help stay within limits, but each interview should still run independently after the query.

That said it's always felt a bit squishy to me and I am reluctant to use pb/flow in cases that will need bulkification.

5
  • This article explains how to manage flow itself. But I am looking for a way to reproduce the flow behavior when launching from apex. It seems extremely weird that we can't launch the flow for apex in the same way it is launched from PB so I assume I just missing something. Commented Jul 29, 2019 at 10:39
  • 1
    I see now, agree it ought to be feasible. Wild guess, but what if you first add all interviews to a list of flow interviews, and then iterate over the list to call the start method. Doubt it would change anything. Good question, hope someone can elaborate.
    – gorav
    Commented Jul 29, 2019 at 12:30
  • I've tried it as well. It work the same way Commented Jul 29, 2019 at 13:01
  • Any Update on this? Commented Apr 18, 2020 at 22:19
  • 4
    I had the same thought to iterate over the list of flows can start sequentially to see if the system is smart enough to bulkify them... It's not. We need a method like Flow.Interview.start(listOfInterviews);
    – NSjonas
    Commented May 13, 2020 at 19:13

You must log in to answer this question.

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