Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I prevent exceptions from causing a transaction rollback under Grails?

My Grails service is having an issue where a swallowed exception unrelated to a transaction is causing the transaction to rollback even when it is unrelated to the persistance of the domain object.

In my service I have something along the lines of

updateSomething(domainObj) {
    def oldFilename = domainObj.filename
    def newFilename = getNewFilename()

    domainObj.filename = newFilename
    domainObj.save(flush: true)

    try {
        cleanUpOldFile(oldFilename)
    } catch (cleanupException) {
        // oh well, log and swallow
    }
}

What I am seeing is that when I have exception when I am cleaning up the old file, I log it and swallow it, but it still causes the transaction to rollback, even though I am already done updating the domain object.

How do I limit the scope transaction to complete before the clean up or is there another way to get the clean up exception to not cause a rollback?

Just for the record I am using Grails 2.1.1

like image 846
James McMahon Avatar asked Apr 04 '13 23:04

James McMahon


People also ask

How do I handle rollback exception?

Usually, you use a rollback exception strategy to handle errors that occur in a flow that involve a transaction. If the transaction fails, that is, if a message throws an exception while being processed, then the rollback exception strategy rolls back the transaction in the flow.

Does transaction rollback on exception?

Note that by default, rollback happens for runtime, unchecked exceptions only. The checked exception does not trigger a rollback of the transaction.

What types of exception does spring automatically rollback a transaction?

Note however that the Spring Framework's transaction infrastructure code will, by default, only mark a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass of RuntimeException. (Errors will also - by default - result in a rollback.)

Does @transactional throw exception?

@Transactional only rolls back transactions for unchecked exceptions. For checked exceptions and their subclasses, it commits data. So although an exception is raised here, because it's a checked exception, Spring ignores it and commits the data to the database, making the system inconsistent.


2 Answers

You can use annotations to do more fine-grained transaction demarcation. By default services are transactional, and all public methods are transactional. But if you use any @Transactional annotations, Grails doesn't make everything transactional - you have complete control.

Runtime exceptions automatically trigger rollbacks, but checked exceptions don't. Even though Groovy doesn't required that you catch checked exceptions, the feature is a Spring thing which doesn't know about Groovy exception handling.

Transactions are implemented by wrapping your service class instance in a proxy. If an exception "escapes" the proxy, whether it's then caught or not, the rollback will have already happened.

So you have a few options. Annotate updateSomething as @Transactional but don't annotate cleanUpOldFile:

import org.springframework.transaction.annotation.Transactional

@Transactional
def updateSomething(domainObj) {
...
}

def cleanUpOldFile(...) {
   ...
}

You can also annotate cleanUpOldFile with one or more unchecked exceptions that shouldn't roll back a transaction (or in other use cases checked exceptions that should), e.g.

@Transactional(noRollbackFor=[FooException, BarException])
def cleanUpOldFile(...) {
   ...
}
like image 89
Burt Beckwith Avatar answered Oct 13 '22 01:10

Burt Beckwith


In addition to @Burt Beckwith's answer, if you have a service where you just don't want transactions (which I actually did in my case) you can turn off transactions on all public methods by adding

static transactional = false

to the Service class.

like image 34
James McMahon Avatar answered Oct 13 '22 01:10

James McMahon