It was to my understanding that when you throw an exception all previous operations are rolled back.
When an exception occurs, code execution halts and any DML
operations that were processed prior to the exception are rolled back
and aren’t committed to the database. Exceptions get logged in debug
logs.
I am running into a situation where I am able to insert a record in a logger class in the catch
and throw an exception after it. What gives?
The flow is: Class A calls Class B -> Class B calls Class C -> Class C has a webservice and a try/catch
public HttpResponse postTokenizer(String securityToken, String payloadJSON) {
try {
if (payloadJSON == null) {
throw new GenericException('Tokenizer was called with no IDs. The ID must be either a Contact or Lead.');
}
HttpRequest tokenizerRequest = new HttpRequest();
tokenizerRequest.setMethod('POST');
tokenizerRequest.setEndpoint(tokensAPIEndpoint);
tokenizerRequest.setHeader('Authorization', 'token ' + securityToken);
tokenizerRequest.setHeader('Content-Type', 'application/json');
tokenizerRequest.setTimeout(30000);
tokenizerRequest.setBody(payloadJSON);
Http http = new Http();
HttpResponse response = http.send(tokenizerRequest);
if (!WebHelper.isSuccessStatus(response)) {
throw new GenericException('There has been an error calling the tokenizer service.');
}
return response;
} catch(Exception e) {
NFLogger.logError('TokenizerHelper, tokenizer', 'Call to postTokenizer() failed.', e); //todo:: how is this inserting????? shouldn't this be rolled back?
throw e;
}
}
NFLOGGER:
global with sharing class NFLogger {
private enum LogLevel {Error, Info}
public static void logError(String tags, String errorMessage){
logMessage(logLevel.Error, tags, errorMessage, null);
}
public static void logError(String tags, String errorMessage, System.Exception ex){
logMessage(logLevel.Error, tags, errorMessage, ex);
}
public static void logInfo(String tags, String infoMessage){
logMessage(logLevel.Info, tags, infoMessage, null);
}
private static void logMessage(LogLevel level, String tags, String message, System.Exception ex){
try {
LogMessage__c newMessage = new LogMessage__c(level__c = level.name(), tags__c = tags, message__c = message, exception__c = ex.getMessage());
insert newMessage;
}
catch (Exception e){
//intentionally we are ignoring "we suppress" any exception
//we need to guarantee that logMessage will never throw any exceptions
}
}
}
Best Answer
The transaction will only be rolled back automatically if the thrown exception isn't caught.
Your example showed a call stack like:
A
calls ClassB
B
calls ClassC
C
attempts a callout to a webservice.Since the resulting
LogMessage__c
record exists after the transaction, there must be acatch
in either classB
orA
that is handling the exception thrown by classC
. This prevents the exception terminating the transaction and causing a rollback of any DML that had occured.It looks like your quote came from Exceptions in Apex, What Happens When an Exception Occurs?. The last part of that paragraph clarifies that the transaction rollback is just for unhandled exceptions. It is probably a bit misleading to imply that a handled/caught exception would also cause a rollback.