Contact
Back to the blog

Process Builders & Timeouts: When Click-and-Configure Goes Wrong

Dec 03 · 6 min read

On the floor of a conference this year, a user approached me at our booth and was wondering if I could help her with a problem she’s having. She described that her users were encountering numerous Apex CPU Timeout errors and that her users often complained about how slow saving records was.

Before she could go any further, I asked: “You have many objects that have several Process Builders on them or objects with a mix of Triggers and Process Builders, right?”. She opened her eyes real wide and told me: “How on earth did you figure that out so quickly?”

This story above illustrates a very common problem in today’s Salesforce organizations, and that is the unchecked growth of point-and-click customizations particularly in small and medium companies that tend to not have an Architect on staff.

Let’s be super clear: Process Builder can be an amazing and empowering tool when used correctly. However, it can slow down your org to a crawl if you’re not careful. Salesforce is doing a fantastic job of promoting PB and its ability to let Admins do things that required a developer a few short years ago, but it is doing a less-than-stellar job in teaching people how to scale them.

Let’s now explore the two most common problems that we find and discuss how to fix them

Triggers and Process Builders on the same object

Having Triggers and Process Builders in the same object is not automatically a problem, but if you are not careful it can really quickly turn into a nightmare. The key to understand this problem is a subject that devs are very familiar with but admins seldom get to learn: Order of Execution.

Salesforce’s Order of Execution list is about 20 steps long and can be found here but I create a shortened and summarized version with the most relevant steps.

1. If this is a UI change, runs UI-specific checks (required values at layout level, field formatting, etc).

2. Executes all before triggers.

3. Runs validation rules and checks required fields

4. Saves the record to the database, but doesn’t commit yet.

5. Executes all after triggers.

6. Executes assignment, auto-response and workflow rules (in that order).

7. If there are workflow field updates, updates the record again. **

8. Executes processes and flows launched via processes and flow trigger workflow actions. **

9. If the record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the parent record.**

10. If the parent record is updated, and a grandparent record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the grandparent record.**

11. Commits all DML operations to the database.

You’ll notice that there are many steps marked with **. That is because those steps can trigger recalculation of the triggered records, their parents or their related records and this, sometimes, can be really bad.

Imagine the following scenario:

A developer created a trigger on a given object that runs certain calculations. Then an admin, unaware of this, creates a Process Builder (PB), or a Workflow Rule (WFR), that makes updates to records when they change. Now a user changes a record (or set of records), and hits save. The system now runs all these records through the trigger, and afterwards, it runs them through the PB (and WFR). Since the PB/WFR changed the records….. the triggers run again!

This is a contrived example but it does illustrate what happens commonly in very many organizations. If the triggers are very lightweight and the PBs have well defined criteria, then the re-running might not cause a problem. But, if the triggers are on the heavy side (think about querying a lot of records and performing updates to them), then running them twice might very easily hit CPU time limits (or SOQL/DML Governor limits, even).

Multiple Triggers or Multiple Process Builders

 Process Builders are very easy to create, and that is a double edged sword. The good thing is anybody can create them, and the bad thing is that anybody can create them.

Most orgs with timeout and limits problem have had very little control over how Process Builders make it into the system. This means that there can be several PB created for the same object, in many cases with a very similar criteria. This forces the platform to have to evaluate the same thing (or a two very similar things) multiple times, causing an un-necessary delay.

What’s worse, Salesforce does not guarantee in which order these Process Builders will get evaluated, so you better not expect any sort of dependency between them (i.e.: expecting that a field populated by PB 1 is going to be present and ready when PB 2 runs for the same object).

The same is true for triggers. While not everybody can create them, it is very easy for developers to accidentally create a new trigger for an object even though one already exists. This is an anti-pattern and should be generally avoided. Triggers have the same problem that Process Builders have when it comes to the guarantee (they can run in any order) and, on top of everything, they will all share the same context. So, the limits still apply to them all together but they’re harder to optimize because, the code being on different files, you can’t consolidate DML and SOQL together.

Having multiple triggers, per se, will not slow down your system, but you will lose valuable opportunities for optimization and it will be significantly harder to track problems if and when they occur.

How do I fix this?

Unfortunately, there is no single “silver bullet” that will fix all problems, but you can take a few steps to minimize, and even fix, this pervasive issue. These steps are, in loose order (there is no requirement to tackle them in that order), simpler than others, and most may require some level of development. My suggestion is that you attack the ones you can and only move to the ones you need help with if your problem still persists. If your org is fairly complex, then the best idea is to engage with an Architect for him/her to take a look and address the problem in a more strategic way.

The steps you can follow are:

1) Narrow the entry criteria to your PBs and WFRs

When you create a PB, you have to specify an entry criteria. Sometimes we “hack” them with rules like Name is not empty because we want the PB to fire all the time and we don’t want tp worry about figuring out the right criteria. If you’re having problems with triggers firing again, this change is a quick way to ensure that PBs only make changes to records when strictly necessary so triggers only re-fire when you absolutely need to.

2) Consolidate multiple PBs/WFRs for the same object

If you have multiple PBs/WFRs in the same object and some share the same (or very close) entry criteria, you should consider consolidating them into a larger Process Builder. Generally, try to have the least amount of Process Builders possible, and try not to have a WFR in an object that already has a PB. A little BA work reducing the amount of distinct entry criteria can have huge positive impacts in performance. And, with the ability to have several conditional nodes into one PB, you should be able to make large consolidations.

3) Review your triggers’ “entry criteria”

Much like your Process Builders, it’s also possible that the triggers might not be checking for “entry criteria”. I put quotes around “entry criteria” because triggers don’t formally require them. However, it’s a good practice to only make changes when necessary. For example: IF you want to run certain logic that is based on the owner of the record, it makes sense to only execute the logic only when the owner changes and not every time the record is updated.

4) Consolidate multiple triggers for the same object

IF you have more than one trigger for the same object, consolidate them into one file. You will be able to optimize them better (see Step #5 below) and you will also be able to precisely predict the order in which they run. This could be also important if you end up moving logic to a “Before trigger” (step #7 below).

5) Review how optimized are your triggers

If your triggers are poorly designed, they might make more queries than they should, or do more DML (database operations) than they should. On large triggers, with many functions, you may be updating the same related records multiple times. Review all of that and try to consolidate together all queries and DML for common objects. That should significantly reduce the time the trigger takes to run. Remember that when multiple records are updated simultaneously (this happens more frequently from Apex, the API or Data Loader) Salesforce will process those in batches of 200. That will tend to compound any inefficiencies that are left behind.

6) Add recursion-protection to your triggers

Recursion protection is a technique to ensure that you code does not fire more than once in a given context. You should carefully look at your code before doing this. Triggers re-fire for a reason, and that reason is that sometimes you need to perform Apex operations on values that were changed in by PBs or WFRs. Since PBs and WFRs run after the trigger (see OoE list above), the re-fire is the only way to get those values.

After determining what parts of your code should run always and which should not, then you can apply recursion protection techniques to those parts of your code that need it. See the snippet below for a quick sample.

It’s important to remark that you may still want to implement step #6 if you are doing updates to triggered records in “after trigger”. If you are updating, say, Lead records in a Lead “after trigger”, then the Lead trigger will immediately re-fire. If you can, move that update logic to a “before trigger” (see step below), but this may not always be possible.

Here is a sample of how recursion protection can work, keep in mind that you need a “helper class” for this technique, because you cannot put static variables in the trigger file code directly:

public without sharing class MyTriggerHelper() {
    public static Boolean hasRun = false;

    public static afterTrigger() {
        recursiveCode();

        if (! hasRun) {
            runOnceCode();
        }

        hasRun = true;
    }

    public static recursiveCode() {
        <your code here>
    }

    public static runOnceCode() {
        <your code here>
    }
}

7) Move Process Builder (or Workflow Rule) logic to “before trigger”

If all the above was not enough, or you simply decided that you don’t want to maintain both triggers and PB/WFR for an object, then you should move all of the logic into a “before trigger”. The advantage of “before triggers” (instead of “after triggers”) is that the records are updated before they are committed so not only triggers will not fire twice (see the previous step) but you will also be able to leverage the values updated by the logic in your “after trigger” (Per our OoE list above, since PBs/WFRs update after “after triggers” run, you can’t see those values in Apex until they re-fire).

8) Create a governance process for Triggers and Process Builders

To be fair, this is not a fix, this is prevention. A governance process doesn’t need to be either overly complicated or cumbersome, and its existence can forestall a lot of the problems I discussed in this article. From a simple “Everybody must check with me before creating new Triggers, Process Builders and Workflow Rules” to establish a board that meets frequently, Governance is ultimately how you ensure that your org never again grows in an unhealthy way that negatively impacts business operations. There are numerous resources on Governance, but I would like to offer here three links that are part of the “Data Architecture and Management” Trailmix put by Salesforce.

In summary, it’s very easy to overdo it when it comes to point-and-click solutions. Follow the advice described in this article and you will be able to minimize disruption and make your org a much nicer place to be.

UPDATE 23-Mar-2020: In Spring 20, Salesforce made “Before-Save Updates in Flows” generally available. This is another new tool to help with the optimization of your processes. I covered that new feature in a new post, that you can find here.

ABOUT THECODERY

theCodery understands the challenges in modern tech stacks. We have developed a personalized approach for each Salesforce Cloud implementation while leveraging our deep been-there-done-that and best-practice expertise to ensure you get the most value from your Salesforce deployment.  We take an agile approach with all development, optimization, and integration projects.  Whether you are trying to broaden your engineering and development capabilities, reduce technical debt, integrate tools you are unfamiliar with, or create new applications, theCodery has a proven track record of solving problems and streamlining complexity.

If you have any questions for theCodery about our team, our process, or the clients, please reach out to us at: https://www.thecodery.io/contact-thecodery

theCodery: Accelerate your time-to-value on Salesforce with a trusted partner that delivers scalable architectures that are tailored to delight your customers.

Other Articles by this Author

theCodery’s Dreamforce Recap – 5 Sessions We Found Most Inspiring theCodery’s Dreamforce Recap – 5 Sessions We Found Most Inspiring
Dreamforce 2021 was held in San Fransisco on September 21st-24th. It was a flurry of presentations, meetings, and activities all centered around the Salesforce ecosystem. Th...
5 min read
We Make Migrating from Klaviyo to Marketing Cloud Easy We Make Migrating from Klaviyo to Marketing Cloud Easy
It may look like a mountainous task from the outside, but the integrations experts at theCodery made it look easy. Learn how we tackled the mountainous task of migrating an ...
6 min read
Is Your Salesforce Driving Operations Or Is It In Need Of An Operation? Is Your Salesforce Driving Operations Or Is It In Need Of An Operation?
A great Salesforce system is constantly evolving to increase performance and drive organizational change. A great Salesforce system is not exclusively consumed with just fix...
5 min read
Salesforce Summer 21’ Release Notes Salesforce Summer 21’ Release Notes
Summer 21’ is just around the corner with releases starting across Salesforce instances as soon as May 15th and deploying to all Salesforce orgs by June 12th...The team at t...
5 min read
theCodery Supports Autism Awareness Month theCodery Supports Autism Awareness Month
Here at theCodery, we are part of a program called Pledge 1%. “Pledge 1% is a global movement that encourages and empowers companies of all sizes and stages to donate 1% of ...
3 min read
What Sets theCodery Apart From Other Salesforce Partners? What Sets theCodery Apart From Other Salesforce Partners?
What sets us apart from every partner that I’ve worked for (or cleaned up after) wasn’t pointed out to me during the interview process, employee orientation, or a pep-rally....
5 min read

Get a {FREE} Consultation Now!

LET'S TALK!