Grails Tip # 6: Service layer transitions

On my last Grails project, as good Grails developers, we faithfully put core application logic inside various service methods. Delightfully, the benefits of doing so paid off and we began re-using services though out the application.

One day, while using our application on a fairly complex set of test data (both in volume and the fact our domain model graph is complex), we stumbled on a huge performance problem. After profiling the controller-action, the problem was traced down to a call to a service method. Simplified, it look like:

class ArticlesController {
   def service

   def handleAction = {
      // stuff ...
      // more stuff ...
      for(int i=0;i < loopLimit;i++){
         service.serviceMethod(someParameters)
      }
      render(view: "article"...)
   }
}

This was a commonly re-used service method, but the interesting part of this method was the fact it was being called within a loop, a loop that could potentially be called several hundred times. The looping itself is not a problem (nor could it be avoided).

By trial and error, we discovered that the actual call to the serviceMethod() was causing the performance issue. If we
1) Simply removed the service method and embedded the application logic in the service method into the loop or
2) Made the service class non-transactional

The performance issue disappeared.  We realized that the under-the-hood work that goes on to setup the transactional service boundry, when there is a complex or large volume of data in the Hibernate session, causes a dramatic perfomrance impact when we repeadly cross the controller- service layer boundry.

The simplest and cleanest solution was to loop inside the service method, thus the application cross the boundary only once. This has the side effect that any errors will roll-back the entire set of data and not just the single record being updated but we were ok with this.

class ArticleService {
   static transactional = true
   def serviceMethod(def params){
      // do stuff..
   }

   def serviceMethodMultiple(def params){
      for(int i=0; i < loopLimit; i++){
         serviceMethod(params)
      }
   }
}

And in the controller:

class ArticlesController {
   def service

   def handleAction = {
      // stuff ...
      // more stuff ...
      service.serviceMethodMultiple(someParameters)
      render(view: "article"...)
   }
}

From the standpoint of a intermediate Grails developer, this isn’t a problem that could have been detected beforehand since it only manifests itself when there is a complex domain model graph already in the session.

We chalked this up as a life lesson. The final solution, avoid any code that may repeatedly crosses into the service layer, especially for services marked as transactional. Instead, create a new method that does the looping inside the service boundary. Identification and avoidance was the best action forward as we were in no position to fix any potential underlying issue with the framework (if there even was any).

Leave a Reply

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