Unit of work, Transactions and Grails

Working with Groovy and Grails often gives you the feeling that things are magic and when you dive in, you realize that things are more complex than expected. At the same time, you often realize that a reasonable default behavior has been chosen by Groovy/Grails framework: What about transactions’ magic in Grails? For me it was hard to believe so let’s try to understand a little more how things work.

For this, Grails documentation is a bit sparse but you had better go and read it to know how and where to declare transactions. We won’t talk about these details here, you can find them in the documentation.

A last remark if you want to play with the different examples, you can find the code in our SVN repository here. The project also includes tests to illustrate the different issues illustrated below.

It’s not that magic

Imagine a classical example, a banking application. You have built your application incrementally and at a few places you have put some business code in your controllers’ actions because the code was too simple to refactor it in a service. You might come with some code similar to this one:

AccountController.groovy

class AccountController {

	def transfer = {

		def fromAccount = params?.fromAccount
		def toAccount = params?.toAccount
		def amountToTransfer = params?.amount

		// some validity checks, could be performed through a Command Object class
		...

		// withdraw from the source account
		fromAccount.balance -= amountToTransfer
		fromAccount.save(flush:true)

		// some logic that raises an error or throws an exception
		...

		// transfer to the target account
		toAccount.balance += amountToTransfer
		toAccount.save(flush:true)
	}
}

In this case, what happens if any error or exception occurs in the ‘some logic’ block? The transfer has been partially performed and committed in the database and a few hundred dollars is lost in the wilderness.

If you know Grails well you could answer that the ‘flush:true’ is, in this particular case, useless and that without it, everything would have worked, and you would be right (see section ‘Hibernate Session in the equation…’ below). But let’s stick to this example for its simplicity, because there are also more complex cases where the flush of the session is automatically performed by Hibernate.

To remember

  • The important thing to remember is that Grails does no automatically wrap each ‘request’ (treatment associated with a HTTP request) in a transaction, so you are not as safe as expected.

At this point you should ask yourself: what is the correct way to handle transactions in Grails?

Grails Model – Business Logic in Services

And you are right. Grails paradigm is to avoid putting business code in the Controller layer but rather in the model or service layer. That’s why all Grails magic for making transactions transparent only happens if you are sticking to this paradigm.

Imagine you refactor the code below in a service (grails-app/services directory):

TransferService.groovy

class TransferService {

	def transfer(fromAccount, toAccount, amount) {

		// withdraw from the source account
		fromAccount.balance -= amountToTransfer
		fromAccount.save(flush:true)

		// some logic that raises an error or throws an exception
		...

		// transfer to the target account
		toAccount.balance += amountToTransfer
		toAccount.save(flush:true)
	}
}

and that you use the standard Grails mechanism to inject this service in AccountController:

AccountController.groovy

class AccountController {

	def transferService

	def transfer = {

		...

		// call service to make the transfer
		transferService.transfer(fromAccount, toAccount, amountToTransfer)
	}
}

This time you will get the expected behavior which is, the balance of the first account won’t be modified and committed if an error happens after the first save(flush:true).

Why? Here comes Grails magic. by default every call to a Service’s method is made transactional. How does this work? Using a combination of Spring IoC for injecting an instance of a service in the controller and using the Proxy pattern to make sure the call is enrolled in a transaction. This behavior is controlled by the Service property ‘transactional’ which is set to true by default.

So if you modify you service code to :

class TransferService {
	static transactional = false

	...
}

you will go back to the phony behavior.

There are a few pitfalls you should avoid when using services, which are:

  • do no create instances of the service, always use injection. If you do:
  • class AccountController {
    
    	def transferService = new TransferService()
    	...
    }

    In this case the calls to TransferService won’t be proxied so you won’t get any transactional behavior.

  • do not use closures in services, always use methods
  • If you were declaring transfer() as a closure like:

    class TransferService {
    
    	def transfer = { fromAccount, toAccount, amount ->
                ...
    	}
    }

    In this case the call won’t be caught by Spring AOP so it won’t be enrolled in a transaction.

Lastly, take great care of what exception you throw because checked exception do not cause the transaction to roll back by default. In Groovy this could be pretty confusing because you are not forced to declare or catch checked exception. To avoid this, you can configure which exceptions cause a roll back at Spring level (using rollbackFor in the @Transactional annotation for example, see this part of Spring documentation).

To remember

  • use services to factor common business logic AND handle correct transaction behavior
  • always use injection to call a service
  • do not use closures to define a service method
  • use runtime exceptions if you want to roll back a transaction

There are other mechanisms to make use of transactions in Grails, mainly through the withTransaction() method but it is not the recommended approach.
You could also have a more fine-grained transaction management if you use Spring @Transaction annotations, but you would leave the magical world in this case.

I am putting all my business code in services, am I safe?

Actually you might. But you better check and understand better the transaction model.

Let me show you an example which surprisingly doesn’t work:

class AccountController {

	def transferService
	def auditService

	def transfer = {

		def fromAccount = params?.fromAccount
		def toAccount = params?.toAccount
		def amountToTransfer = params?.amount

		// some validity checks, could be performed through a Command Object class
		// ...

		// call service to make the transfer
		transferService.transfer(fromAccount, toAccount, amountToTransfer)

		// notify the auditing plateform of the transfer
		auditService.notifyTransfer(fromAccount, toAccount, amountToTransfer)
	}
}

So what? Imagine the notifyTransfer() call fails (audit server might be down), an exception is then thrown and the transaction is rolled back. But the question is: which transaction? Because actually there are two different transactions in this transfer() action. One around the transferService() and one around the notifyTransfer() call. Oops! Which takes us to our initial finding: it’s not that magic…

Why are there two transactions? We won’t talk long about this but to make the story short, the default transaction propagation on services’ methods is REQUIRED (see this doc) so a new transaction is created when entering transfer() and committed when exiting transfer(). The same applies to the notifyTransfer() call. See picture below for a better understanding of what is going on:


Figure 1

Doc’, what should I do?

The obvious answer is to stick to Grails paradigm and make sure your unit of work is always encapsulated in the same transaction. In the case above, the natural way of solving it would be to put the notifyTransfer() call in the transferService.transfer() service call. If this breaks your object encapsulation, you will have to create a new service that will take care of calling both transfer() and notifyTransfer().

To remember

  • Stick to Grails paradigm of putting transactional code in the services
  • Always put your unit of work in a top-level service, aka don’t call two services in a controller action

Hibernate Session in the equation…

When Hibernate session comes into the game, everything mentioned above becomes true or false. As a quick reminder, the session management in Grails is based on the Open Session In View pattern (OSIV, see this doc). The thing to remember is that Hibernate Session is not flushed if an exception is thrown which is the reason why we put save(flush:true) in the first example.

This enables you to put read/write code in your controller action without fearing of having an inconsistent state in the database but it is actually a false sense of security. Indeed, this feature falls apart when your are calling services because committing a transaction flushes your Hibernate session. Or when you really have to flush the session because you need to.

Another pitfall occurs if you read a state in your controller action (thus outside of any transaction) and you then call a transactional service. You then loose the isolation level defined at your transaction level, which is for sure not what you wanted at first.

To remember

  • It is OK to rest on Hibernate session and to put simple logic in your controller, as long as you don’t call any service in your controller.
  • You should not load objects outside of a transactional service and use it as an argument provided to the service

Quest for a better world…

Isn’t there any easier way, any more magic, that could turn transaction management in a really reckless job, at least in Grails?

One solution could be to use the ‘one request one transaction’ pattern, in the same way Grails use the OSIV pattern. The trick would be to open the transaction before the controller action is called, and to commit it after the action returns (but before the view is rendered).

One way to do this could be to add a filter taking care of starting and committing the transaction. For example, you could add a TransactionFilters in grails-app/conf directory:

TransactionFilters.groovy

class TransactionFilters {

	def sessionFactory

	def filters = {

		startTransaction(controller:'*', action:'*') {
			before = {
			    sessionFactory.getCurrentSession().beginTransaction()
			}
		}

        // no need for an 'after' filter, Spring take care of committing or rolling back
        // the transaction

	}

}

With this filter in place, the schema shown in Figure 1. will become:


Figure 2

What impact does it have? This would definitively have a very small impact on performance as the ability to make use of read-only transactions or non-transactional requests would be lost. But in my opinion the performance gain is so minor that you would definitively stop using Groovy and Grails if you really had such a performance critical application in production. Of course all this is not applicable to the batch processing part of your application. In that case, the transaction management will be completely different and not tied to any HTTP request, but this is out of the scope of this article.

One last point: even if this filter does simplify transaction management, it’s always better to have a good understanding of how transactions work and not just to rely on too much magic.

That’s it, I hope I did not lose you on the way. And feel free to leave a comment and ask for clarification.


10 commentaires sur “Unit of work, Transactions and Grails”

  • Thanks for this great article. I found no good guide about transaction in grails and was confused. After reading your article, now I have clear idea about transaction management in grails.
  • clear and very helpful summary ... this info should make it into the grails-documentation. and i'll welcome your article about batch-processing with grails ;-) thanx
  • Great Article, I was wondering if there's a way to change the default transaction propagation on service methods, on a per method basis, or if not possible at least on a service basis. For example, ServiceA could have 2 methods method1 and method2, a would like to tell grails (or hibernate, or both) to use Propagation.REQUIRES_NEW for method1 and stick with Propagation.REQUIRED for method2. Is this possible? Thanks in advance, Regards, Diego Bendlin
  • @Diego, thanks for your feedback. You can use Spring @Transactional annotation to do what you want. Basically, you can use it on a per-method basis or on a per-class basis: import org.springframework.transaction.annotation.* class ServiceA { @Transactional(propagation = PROPAGATION_REQUIRES_NEW) def method1() { [...] } // this method is PROPAGATION_REQUIRED by default def method2() { [...] } } or on a per-class basis: @Transactional(propagation = PROPAGATION_REQUIRES_NEW) class ServiceA { } See Grails doc §8.1 'Custom Transaction Configuration' in http://grails.org/doc/latest/ or related section in Spring documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#transaction-declarative-annotations.
  • Fantastic arcticle and vert clearly explained. You should make this a series and explain how best to use transaction = false and withTransaction in certain situations.
  • Thanks for the Great Article. I was just thinking to get rid of the Transactions magic in Grails and manage the transactions in service layer as this will give more control over the service layer. This article pushes me towards the said approach. Can we also configure whether the Hibernate Session in GORM is Thread Scoped or JTA scoped?
  • I wanted to apply the proposed approach in one of my projects, but I have realized that transactions are not rolled back if RuntimeException (and perhaps some others too) occurs somewhere in code. Hence, I have applied another idea - I wrote a plugin that wraps controller's actions in transactions, just like withTransaction dynamic method does it. The further instructions and plugin can be found here: http://grails.org/plugin/transactional-controller
  • thanks for the pointer, Predrag. On what version of Grails did you try this? I can confirm that a RuntimeException was rolling back transactions in Grails 1.1.2. Maybe there is an issue with more recent versions, I haven't had the opportunity to test this against a more recent version. Did you try to see if the filter was correctly called?
  • @Tarun, I must say I did not completely get your last question. My understanding is that Grails uses the Open-Session-In-View pattern so the session is request-scoped rather than transaction-scoped. Out of the box in Grails, you can have multiple transactions in one request. Of course, the session is flushed before a transaction is committed. But what do you mean by 'is the Hibernate Session Thread-Scoped'? If you think in terms that a thread handles a request, I guess we could say yes. But be aware that the thread is not stopped but re-used in the application server, while the session is not.
  • Useful, and well explained. Thanks
    1. Leave a Reply

      Your email address will not be published. Required fields are marked *


      This form is protected by Google Recaptcha