Basically what I’m trying to do is to insert an error record (custom object) when
an error occurs.
To test if this is working I created a simple test where I try inserting a contact that
has a reference to an account that I have just deleted. This throws an exception, which
I catch. Then I create an error record with information from the exception.
Now, if I do not have a reentrancy flag in the class to run from the test my error record gets saved to the database (in fact it gets saved twice for a contact insert, once for beforeInsert and once for afterInsert). But if I have the reentrancy flag, it doesn’t get saved at all, even though I see in the logs that it is saving it!
I actually created a simple test to reproduce this in the project where I am testing some of my error handling stuff. You can go to https://github.com/SalesforceFoundation/Error_Handling,
install it in a brand new org, and try running the test in ERR_Handler_TEST.cls. The test will fail because it does not find any error record, even though you’ll see in the logs that it is saving it.
If you comment out the lines in the ParentAccountUpdater class that use the parentAccsAlreadyUpdated variable (3, 7, 29 and 30) you will see that the test fails too, but now it’s because it finds two records instead of one.
Any ideas of why this might be happening?
EDIT – To clarify, the only files that intervene in this scenario are ERR_Handler_TEST.cls, ERR_Handler.trigger, and ParentAccountUpdater.cls. The rest of the files can be ignored, since they are there for some other tests I was working on.
EDIT – To summarize:
A. If parentAccsAlreadyUpdated flag in ParentAccountUpdater class is commented out:
Assertion Failed: Expected: 1, Actual: 2 (in line 53)
–> This makes sense. One record saved for the exception thrown during the before insert and one for the after insert
B. If parentAccsAlreadyUpdated flag in ParentAccountUpdater class is NOT commented out:
Assertion Failed: Expected: 1, Actual: 0 (in line 53)
–> This does NOT make sense. One record should have been saved. And we see in the logs that it’s trying to save it:
[...]Database.insert(LIST<SObject>, Boolean) [...]|DML_BEGIN||Op:Insert|Type:SObject|Rows:1
The only explanation I can give is that the whole thing gets rolled back.
The very bright David Habid (creator of Volunteers for Salesforce) has helped me figure it out. What seems to be happening is as follows.
Let’s say that you have 3 records that you want to insert with a Database.insert call. If one of them fails Salesforce will roll back all database-related operations and then do the insert again with the 2 that succeeded. HOWEVER, if you have a reentrancy flag, the value of the flag will not be rolled back (after all roll backs affect only the database). Therefore the second time nothing will happen (the reentrancy flag prevents the code from running again).
You see in the logs that the error records are being created, but that is all happening in the first context, which is fully rolled back.
To my knowledge it is not fully documented that this is how the Database methods work, but this seems to be consistent will all the tests that I have been doing.
UPDATE: For some very interesting examples of how #Salesforce’s Database.insert method works take a look at these tests.