Loading
Salesforce now sends email only from verified domains. Read More
Automate Your Business Processes
Table of Contents
Select Filters

          No results
          No results
          Here are some search tips

          Check the spelling of your keywords.
          Use more general search terms.
          Select fewer filters to broaden your search.

          Search all of Salesforce Help
          Bulkified Record Processing to Avoid CPU Limits Example

          Bulkified Record Processing to Avoid CPU Limits Example

          Learn how to refactor flows to use bulkification patterns that prevent APEX_CPU_TIME_LIMIT_EXCEEDED errors. This example demonstrates the difference between inefficient and optimized flow design.

          Required Editions

          View supported editions.
          User Permissions Needed
          To open, edit, create, activate or deactivate a flow using all flow types, elements, and features available in Flow Builder, including Einstein and Agentforce for Flow: Manage Flow

          This example shows how to transform a flow that performs Data Manipulation Language (DML) inside a loop into a bulkified flow that processes records efficiently. View a before-and-after comparison and learn the principles of flow bulkification.

          Scenario

          Build a record-triggered flow that runs when an opportunity closes. For each closed-won opportunity, the flow creates a follow-up task for each member of the opportunity team. The flow:

          • Gets all opportunity team members.
          • Creates a task assigned to each team member.
          • Works efficiently even when users update multiple opportunities at once.
          • Avoids APEX_CPU_TIME_LIMIT_EXCEEDED errors during bulk operations.

          Salesforce enforces a single CPU time limit of 10,000 milliseconds (10 seconds) per synchronous transaction. Every piece of automation in that transaction—Apex triggers, flows, workflow rules, and processes—draws from the same budget. An inefficient flow design compounds the problem. Other automations in the transaction draw down the shared budget first, and an inefficient flow consumes more than necessary of what remains.

          The order in which flows run depends on the flow type. This example uses an after-save record-triggered flow, which runs later in the order than before-save flows. For details on run order, see Triggers and Order of Execution.

          Inefficient Approach: DML Operations Inside a Loop

          This approach causes CPU limit errors when processing multiple opportunities.

          Flow Structure: The flow starts with a Record-Triggered Flow on the Opportunity object that runs after a record update when Stage equals "Closed Won." The flow uses Get Records to get Opportunity Team Members for the triggering record, then loops through the collection. Inside the loop, a Create Records element creates one task for each team member.

          Why This Approach Fails:

          • Each loop iteration performs a separate DML operation.
          • An opportunity with 5 team members results in 5 DML operations.
          • When users update 20 opportunities at once, the flow processes all opportunities in one transaction.
          • 20 opportunities × 5 team members each = 100 DML operations in one transaction.
          • The cumulative CPU time from 100 DML operations exceeds the governor limit.
          • Result: APEX_CPU_TIME_LIMIT_EXCEEDED error.

          Efficient Approach: Collection-Based Bulkification

          This approach processes records efficiently regardless of volume.

          Flow Structure: The flow starts with a record-triggered flow on the Opportunity object and uses Get Records to get Opportunity Team Members. The flow creates a Task record variable and Task record collection variable. The record variable holds the current task that the flow is working on and the record collection holds the tasks to be created. The flow loops through the team members. Inside the loop, an Assignment element builds each task record by setting values for the Task record variable and relates the task to the current team member in the loop. Another Assignment element adds the Task record variable values to the Task record collection. After the loop completes, a single Create Records element creates all tasks from the collection.

          Why This Works:

          • The loop builds a collection of task records without performing DML operations.
          • Only one DML operation occurs after the loop completes.
          • An opportunity with 5 team members still results in just 1 DML operation (creating 5 tasks at once).
          • When users update 20 opportunities at once, the flow performs 20 DML operations in total (one per opportunity).
          • Significant reduction in CPU time consumption.
          • Result: Flow completes successfully without hitting governor limits.

          Performance Comparison

          Scenario Inefficient (DML in Loop) Efficient (Bulkified)
          1 opportunity with 5 team members 5 DML operations 1 DML operation
          20 opportunities (bulk update) with 5 team members each

          100 DML operations

          (Likely fails with CPU limit error)

          20 DML operations

          (Completes successfully)

          100 opportunities (data load) with 3 team members each

          300 DML operations

          (Definitely fails)

          100 DML operations

          (Completes successfully)

          Key Principles of Bulkification

          • Never perform DML operations inside loops. Always collect records in a collection variable during the loop, then perform DML after the loop completes.
          • Design for bulk operations from the start. Assume that record-triggered flows process multiple records simultaneously.
          • Use record collection variables. Collection-based DML operations are dramatically more efficient than individual operations.
          • Test with realistic volumes. Debug mode with one record doesn't reveal bulk operation issues. Test with Data Import Wizard, Data Loader, or mass updates.
          • Monitor performance. Review debug logs to identify flows approaching governor limits before they fail in production.

          High Level Steps

          Review the steps of this example. Follow them in order or go to a section for more specific instructions.

          Create the Flow

          Set up the record-triggered flow to run when opportunities close.

          1. Open the Flows list view.
            • From Setup, in the Quick Find box, enter Flows, and then select Flows.
            • From the Automation app, select the Flows tab.
            • From the Flows tab in any Lightning app, click the actions menu and select Open Flow.
          2. Create a record-triggered flow.
            1. From the Automation Lightning app, click New. Search for and select Record-Triggered Flow.
            2. From Setup, click New Flow, search for and select Record-Triggered Flow.
            The Configure Start panel opens.
          3. Configure the Start element.
            1. For Object, select Opportunity.
            2. For Trigger the Flow When, select A record is updated.
            3. In the Set Entry Conditions section, For Condition Requirements, select All Conditions Are Met (AND).
            4. For Field, selectStage.
            5. For Operator, select Equals.
            6. For Value, select Closed Won.
            7. For Optimize Flow, select Actions and Related Records.

          Create Resources

          Create the variables needed for bulkified processing.

          1. To build each task, create a record variable for building task records.
            1. To open the Toolbox, click Toolbox icon..
            2. Click New Resource, and then select Variable.
            3. For API Name, enter TaskRecord.
            4. For Data Type, select Record.
            5. For Object, select Task.
            6. Click Done.
          2. To store all tasks, create a record collection variable for all tasks.
            1. Click New Resource, and then select Variable.
            2. For API Name, enter TasksToCreate.
            3. For Data Type, select Record.
            4. For Object, select Task.
            5. Select to Allow multiple values (collection).
            6. Click Done.

          Get Team Members

          Get the Opportunity Team Member records for processing by using a Get Records element.

          1. Click Add element plus icon, and then search for and select Get Records.
          2. Configure the element.
            1. For Label, enter Get Opportunity Team Members.
            2. For Object, select Opportunity Contact Role.
              In this example, the flow stores team members in the Opportunity Contact Role object. An object that links opportunities to contacts.
            3. In the Filter Opportunity Contact Role Records section, For Field, select Opportunity ID.
            4. For Operator, select Equals.
            5. For Value, select Triggering Opportunity and then Opportunity ID.
            6. For How Many Records to Store, select All records.
            7. For How to Store Record Data, select Automatically store all fields.

          Add a Decision to Check for Team Members

          Add a Decision element to avoid processing when there are no team members.

          1. Click Add element plus icon, and then search for and select Decision.
          2. Configure the decision.
            1. For Label, enter Found Team Members?.
            2. The API name auto-fills.
            3. For Outcome Label, enter Yes.
            4. The API name auto-fills.
            5. For Resource, select Opportunity Contact Roles from Get Opportunity Team Members.
            6. For Operator, select Is Null.
            7. For Value, select False.
            8. Click Default Outcome.
            9. For Label, enter No.

          Add a Loop Element

          Add a Loop element to iterate through team members.

          1. In the Yes path, click Add element plus icon, and then search for and select Loop.
          2. Configure the loop.
            1. For Label, enter Iterate Through Team Members.
            2. The API name auto-fills.
            3. For Collection Variable, select Opportunity Contact Role from Get Opportunity Team Members.
              This resource shows as singular, even if it contains multiple records.
            4. For Direction, select First item to last item.

          Build Task Records Inside the Loop

          Populate field values for the TaskRecord record variable for each team member inside the loop.

          1. After For Each, click Add element plus icon, and then search for and select Assignment.
          2. Configure the assignment.
            1. For Label, enter Build Task Record.
            2. The API name auto-fills.
            3. For Variable, select TaskRecord and then select Subject.
            4. For Operator, select Equals.
            5. For value, enter Follow up on closed opportunity.
            6. Click + Add Assignment.
            7. Continue adding assignments and setting values.
              Resource Operator Value
              TaskRecord>Status Equals Not Started
              TaskRecord>Priority Equals High
              TaskRecord>Assigned To ID Equals Current Item from Loop>Contact ID
              TaskRecord>Related To ID Equals Triggering Opportunity>Opportunity ID

          Add Tasks to the Collection

          Add each task record to the TasksToCreate collection variable for bulk creation.

          1. After the Assignment element, and still in the loop, click Add element plus icon, and then search for and select Assignment.
          2. Configure the assignment.
            1. For Label, enter Add Task to Collection.
            2. For Variable, select TasksToCreate.
            3. For Operator, select Add.
            4. For Value, select TaskRecord and then Entire Resource.

          Create All Tasks After the Loop

          Use a single Create Records element to create all tasks in one operation.

          1. After the loop ends, click Add element plus icon, and then search for and select Create Records.
          2. Configure the element.
            1. For Label, enter Create All Tasks.
            2. For How to set record field values, select From a Record Variable.
            3. For How Many Records to Create, select Multiple.
            4. For Record Collection, select TasksToCreate.
          3. Save the flow.
          4. For Label, enter Create Tasks for Opportunity Team Members.
          5. The API name auto-fills.

          Add Error Handling

          Configure fault management to capture and report errors.

          1. To manage errors, add a fault path to the Create Records element.
            1. Click the three dots on the Create All Tasks element and select Add Fault Path.
          2. To notify Salesforce admins of failures in the fault path, add a Send Email action.
            1. On the fault path, click Add element plus icon, and then search for and select Send Email.
            2. For Label, enter Notify Admin About Error.
            3. The API name auto-fills.
            4. For Recipient Addresses, enter the email address of the person you want to receive the notification.
            5. For Subject, enter A flow needs your attention
            6. For Body, enter The Create Tasks for Opportunity Team Members flow had an error. Here's the error: .
            7. Click Insert a resource.
            8. Select Running Flow and then select Fault Message.
          3. Save your work.

          Test with Bulk Data

          Testing bulkified flows requires simulating bulk operations.

          1. To test an individual record, use Debug mode to update one opportunity and verify that the flow creates tasks correctly.
          2. Activate the flow.
          3. To test bulk operations in a sandbox, use the Data Import Wizard, Data Loader, or a list view to update multiple opportunities at once.
            1. Create between 10 and 20 test opportunities.
            2. Add team members to each opportunity.
            3. Use the Data Loader or mass update to change the stage to "Closed Won" on all opportunities simultaneously.
            4. Verify that the flow creates all tasks without errors.
          4. To verify performance, review the Apex debug log to confirm that CPU time consumption is within acceptable limits.

          More Optimization Techniques

          Further optimize bulkified flows with these techniques.

          Use Entry Conditions to Limit Execution

          Add conditions to the flow Start element to make sure that it runs only when necessary. For example, process only opportunities over a certain amount or from specific record types.

          Consider After-Save for Non-Critical Operations

          After-save record-triggered flows can create follow-up tasks after you save the opportunity. They run asynchronously and have higher governor limits.

          Batch Process for Very Large Volumes

          If opportunities regularly have dozens of team members, use a scheduled flow that processes closed opportunities in batches rather than in real-time.

          Convert Existing Flows

          Convert flows with Data Manipulation Language (DML) operations in loops to bulkified patterns.

          If you have existing flows with DML operations in loops, follow these steps to convert them.

          1. To find DML operations in loops, identify all Create Records, Update Records, and Delete Records elements inside loops.
          2. For each DML element, perform the conversion.
            1. To build records, create a record variable for that object type.
            2. To store the records you want to process, create a record collection variable for that object type.
            3. To save field values for the records, use an Assignment element to assign values to the record variable.
            4. To collect the records to process, use an Assignment to add the record variable to the record collection.
            5. To create records efficiently, after the loop, add one DML element that processes the entire record collection.
          3. To verify the conversion, test thoroughly with bulk data to verify that the refactoring worked correctly.
           
          Loading
          Salesforce Help | Article