Unexpected rollback behaviour

I have a page that saves three models when a button is clicked. The first model is just one record being created; the second (records being created) and third (records being updated) have many more. And more significantly, the second and third models have a lot of automation tied to them - mainly Processes and Flows.

All three models are saved within one action, and that action has the “Roll back save on any error” option checked. Because of the automation, I occasionally get a “Apex CPU time exceeded” error back from the server (this is visible in the Javascript console of the browser, and looks like it originates from the remoting call).

(Side ntoe: I do occasionally see other errors - I can’t remember which ones exactly, but I recall them being timeout type issues. I’d imagine this could be put down to a network problem, and that the operation succeeded on the server, but the client didn’t receive a response, so it’s a different issue. I only mention it for completeness :slight_smile:

So with the CPU error, and the “Roll back” option set, what do you think happens with the data? I’d expect that none of the data is operated on - as Limit exceptions can’t be caught, I don’t expect Skuid to be able to be able to do too much in this scenario. However, when an error occurs, what I actually see is that some of the saves take place. For example, the second model’s records are created, but the third model’s records are not updated.

I’m as certain as can be that the Skuid front end is making one remoting call i.e. the backend handles all three model saves in one remoting call; when a CPU limit exception occurs, I’d expect all data updates to be rolled back by the platform, but that doesn’t appear to be what’s happening. If Skuid is somehow being smart (maybe in the back end there are checks against the limits and Skuid raises a custom exception - and that’s what I’m seeing in the JS console?), then the “Roll back” checkbox isn’t being respected, which it should be.

Just to be clear - I’m not asking about solving the Apex CPU error. I know the ways in which I can reduce the amount of time on the CPU and therefore reduce the chance of that error happening. I’m more interested in understanding what’s happening in the Skuid backend for this to happen as it does.

Look forward to hearing theories about this - thanks!

Hi Gary,

Skuid’s “Roll back save on any error” is meant to work on “Record Level Errors”. These are errors created by Apex Triggers or Validation Rules because of some violation of your underlying database logic. 

As for Apex Errors such as limit exceptions, Skuid does not use any kind of clever logic to catch these errors. I’ve always been under the impression that if a real Apex Error occurs, any DML that happened before the exception in that transaction scope would be rolled back. I have seen this over and over again with Triggers. If an after insert trigger bombs, the insert will be rolled back.

The behavior you’re seeing is definitely intriguing and contrary to everything I’ve seen. The only thing that I can think of is that somehow the Processes and Flows are in a different transaction context and that when they fail, they aren’t rolling back the operations that took place inside Skuid’s Apex code (Which they totally should in my opinion).

Can you post an image of the javascript remoting error you’re getting in your browser’s console? Do you know if the error is happening in a lightning process builder process? Since that is a newer technology than Triggers (what I’m used to) maybe it behaves a bit differently.

Hi Ben!

Thanks for the confirmation on expected behaviour. As you may have gathered, the is a bit of a pain to reproduce, so I’m not currently in a position to provide follow ups, so I was looking more for a confirmation on my points, and such your reply is right on the money.

We’re going to be looking at this in the coming weeks (we have workarounds for now whilst we tune the performance), so I’ll report back.

Regarding your first point:

“Skuid’s “Roll back save on any error” is meant to work on “Record Level Errors”. These are errors created by Apex Triggers or Validation Rules because of some violation of your underlying database logic”

So can I just check: if I save multiple models, and the last model that is saved has an error, is the database rolled back so none of the updates apply? i.e. in the back end is Skuid doing something like:

sp = Database.createSavepoint();<br>try {<br>&nbsp; for(Model m : modelsThatNeedSaving) {<br>&nbsp; &nbsp; saveModel(m);<br>&nbsp; }<br>} catch (Exception e) {<br>&nbsp; Database.rollback(sp);<br>}

Hi Gary,

Yes, it is like that, except that we don’t use a try/catch block. We split the operations from each model into insert, update, and delete operations and perform them separately. With each operation, Salesforce returns a Database.SaveResult object that lets us know if any errors occurred in each operation. These errors are not “true” Apex errors, just database errors, so they do not need to be handled like regular exceptions. If the SaveResult has errors, then we do a Database.rollback to the savepoint.

Not having a try/catch block around this code is intentional. We don’t want to catch those errors, so that your browser gets them in their original form and Apex will rollback any DML automatically. This why I find the behavior you are reporting so strange.

Thanks Ben, that’s great.

I hope to be in a position to try and re-create the error at some point in the not too distant future - if I uncover anything, I’ll let you know. For now, please mentally file this under “Probably developer error or talking nonsense” :wink:

We’d never file it away in that category!   Thanks for asking the question and letting us show a little more whats going on under the covers.