9

If one publishes a Platform Event, the consuming trigger executes as Automated Process user. If that consuming trigger exceeds CPU limits, no notification is sent to the developer(s).

Even though Apex Exception Email is configured to route to me

Even though my user is configured to get Apex Warning Emails

How does one discover that there was a limits exception so action can be taken?

Platform Event Foo__e

Apex Trigger

trigger FooTrigger on Foo__e (after insert) {
    Util.sleep(30); // method that does a CPU sleep of ~arg secs 
}

DeveloperConsole

EventBus.publish(new Foo__e());

I have no idea where to find the limits exception email or other indication of failure; querying EventBusSubscriber yields no information re: errors of this sort.

Support ticket filed

UPDATE:

Lesson to us all: Since a Limits failure fails silently when running user = Automated Process, and since your platform event trigger is bulkified; if one event in your batch is, say, exceptionally CPU or heap intensive, the other batch members will fail (silently) as "collateral damage" and be undetected until you do a database audit. Adrian Larson's answer is a practical design way out of this.

Ask me how I know :-(

2

My plan to work around this limitation would be roughly as below.

Configuration

  1. Use a logging object to track each Platform Event you process in your trigger.
    • Make sure this logging object has no required fields nor validation rules.
  2. Add a Text (18) field to track Job_Id__c.
    • You can later use this field to query AsyncApexJob.
  3. Add a Text field to track the Job_Status__c.
  4. Add a TextArea field to track the Job_Error_Message__c.

Code Changes

  1. Move your core logic to a Queueable.
  2. From your subscriber trigger, fire this async job so any steps which can fail will take place in a separate transaction.
  3. From your subscriber trigger, insert a record into your log object.
  4. On your log record, set the Job_Id__c field.
  5. Set up a scheduled batch which iterates over any log records whose Job_Status__c is in (null, 'Holding', 'Preparing', 'Processing').
    • Match each Job_Id__c up to the corresponding AsyncApexJob.
    • Map Status to Job_Status__c.
    • Map ExtendedStatus to Job_Error_Message__c.
  • N.B. this useful pattern has a side effect of eliminating the utility of EventBusRetryableException retry on transient errors like UNABLE_TO_LOCK_ROW – cropredy Dec 15 '18 at 0:49
  • Can't you replay the event anyway if you track its ReplayId on your log? @cropredy – Adrian Larson Dec 15 '18 at 2:03
  • Yes, there are workarounds. – cropredy Dec 15 '18 at 4:38
  • 1
    I eventually implemented this answer ; note for others: when core logic is in a queueable, if core logic needs to start other (chained) queueables, you are limited to only one child. There are workarounds for this of course. – cropredy May 24 at 0:34
3

A very unsatisfying answer from Support:

As discussed in the call, the Update that i got from my Product Team is that if a Transaction is executed under the Process Automated User and has failed out due to an Apex Time out limit then there is not gona [sic] be an email sent out as the Automated User doesn't have the level of access to send an email.

and follow up from support ...

As discussed in the call, the Automated Process User has many process that are runned [sic] under him and if there is a failure because of that then the organisation is gonna [sic] get many emails if there is anything failing out.

You can't have a Production system where if a transaction fails (while running as Automated Process User) due to an uncatchable Limits exception and nobody gets notified.

Possibly related (same root cause ?):

UPDATE - Spring 19 may have a partial solution to this (props to @danielballinger for pointing this out on Twitter)

The new Apex Unexpected Exception event type in the EventLogFile object captures information about unexpected exceptions in Apex code execution. The standard way to obtain exception information is from generated email. However, you now have the option of analyzing the EventLogFile object for Apex exception information, including stack traces.

This is most likely an extra cost item as EventLogFile is part of Shield

https://releasenotes.docs.salesforce.com/en-us/spring19/release-notes/rn_security_em_eventlogfile_apex_unexpected_exception.htm

0

I do not know direct answer for your question, but I'll post answer just for future reference providing temporal (I hope) workaround.

I went through documentation, developer guides etc, tested what SOQLing EventBusSubcriber shows and didnt find anything that could help and is normally configurable.

So I ended with doing custom solution for that using Limits class, custom object and workflow rule.

So event trigger looks like that (example on DML limits)

trigger EventTest on Test__e (after insert) {
   for(Integer i = 0; i < 250; i++){
       if(Limits.getDMLStatements()<Limits.getLimitDMLStatements()-1){
           insert new Account(Name = '5Test' + i);
       } else {

           insert new Error__c(Message__c = 'Limtis reached');
           break;

       }

   }
}

Im using Limits class to check if we are reaching limits and if yes I'm inserting custom object (which have field that have email field with default value to email address I want to send to message) and well message.

Rule is firing on record insert.

For workflow rule sender I used org wide email address because Automated Process cant send emails.

Unfortunately I work with limits here like with exception handling. Its troublesome if triggers would be complicated but works.

  • 1
    yes - avoiding Limits exceptions is of course the best policy. This is somewhat easier to do with SOQL and DML limits and much harder to do with CPU limits as one can not forecast in advance how much CPU time a given statement will incur – cropredy Dec 12 '18 at 17:34
  • Yeah true that would involve counting time complexity of operations for specific data volume(200 records?) in Salesforce context. Which would be simply pain in the.... – user1974566 Dec 12 '18 at 19:26

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.