13

Steps to reproduce:

  1. Create Visualforce Email Template
  2. Create Visualforce component and add it into the email template
  3. Create Apex Controller for the component from pt.2
  4. Do some SOQL queries there, process them and result pass into component markup.
  5. Generate an email using Messaging.renderStoredEmailTemplate many times and get results from the email body. One email template generation takes 1 SOQL query limit, controller SOQL query limits are not counted.

LimitsHack.email

<messaging:emailTemplate subject="Limits Hack" recipientType="User" relatedToType="Contact">
    <messaging:plainTextEmailBody>
        <c:LimitsHackCmp/>
    </messaging:plainTextEmailBody>
</messaging:emailTemplate>

LimitsHackCmp.component

<apex:component id="LimitsHackCmp" controller="LimitsHackCmpController" access="global">
    {!results}
</apex:component>

LimitsHackCmpController.cls

    public String results {
        set;
        get {
            String result = '';
            for (Integer i = 0; i < 100; i++) {
                List<Account> accs = [SELECT Name FROM Account LIMIT 1 OFFSET :Math.mod(i, 3)];
                result += i + ') ' + accs[0].Name + ' ';
            }
            return result;
        }
    }
}

LimitsHackPlayground.cls

public with sharing class LimitsHackPlayground {
    public static void execute() {
        EmailTemplate emailTmpl = [
                SELECT Id
                FROM EmailTemplate
                WHERE DeveloperName = 'LimitsHack'
        ];

        System.debug(UserInfo.getUserId());

        for (Integer i = 0; i < 35; i++) {
            Long renderStart = System.now().getTime();
            String emailBody = Messaging.renderStoredEmailTemplate(emailTmpl.Id, UserInfo.getUserId(), '<any contact id>').plainTextBody;
            System.debug('LimitsHackPlayground: i=' + i + '. Took ' + (System.now().getTime() - renderStart) + ' ms. Email Body: ' + emailBody);
        }
    }
}

OFFSET :Math.mod(i, 3) I used to avoid any type of caching because of the deterministic repeatable query. Each email generation takes up to 500ms, so it is easy to get into the Apex CPU time limit. But still, I managed to make 35 email renders.

Question: Is it expected behavior or a bug? Did anybody meet similar behavior before?

6
  • What would be the non-buggy expected behavior, in your opinion?
    – identigral
    Dec 15, 2019 at 23:33
  • @identigral I would say Salesforce limits should apply to every synchronous query in a context. Dec 18, 2019 at 5:26
  • 4
    If each call to Messaging.renderStoredEmailTemplate is a separate transaction, the per-tx limit of 100 SOQL queries is still good. Does it complain if you go over 100 in the controller?
    – identigral
    Dec 18, 2019 at 19:00
  • Result of calling renderStoredEmailTemplate is returned synchronously. It's not a separate transaction. Dec 19, 2019 at 7:22
  • 4
    @YevhenKharchuk whether its synchronous or not has nothing to do with whether or not it is a transaction. The emails are acting a bit like Visualforce page renders in this context. so its a bit like rendering 35 visualforce pages, each with 100 SOQL queries on it. Nice experiment though! Eventually you will hit the email limit I guess!
    – codeulike
    Feb 3 at 15:59

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.