Increasing Efficiency in Grails

Modern web applications are counting on a large number of users, so efficiency is a very important aspect of web development. Today, we’ll talk about the ways to increase the efficiency of the web application developed on the Grails platform.

Let’s imagine that we are developing a web service that helps to sell used cars. And we need to write a request that will return the number of cars that had been produced between two dates and in the specified region, i.e., the number of cars that had been produced in Asia between 1990 and 2000. This example is written for using the MongoDB database and tested on the MongoDB Grails plugin, version 1.0.0.M6.

Let’s first take a look at the domain objects that will be used in our example:

class Car {
   ObjectId id
   String brand
   String fullName
   long productionDate
   String serialNumber
   Country country
}

class Country {
   ObjectId id
   String name
   String region
}

The first thing to increase database collection speed is adding indexes. Let’s say the unique ID for the Car collection is the car serial number. So you should add an index for this field by specifying it in the Car domain class:

static mapping = {
   serialNumber index: 'serial_number_index'
}

Now, let’s take a look at a Country class. Obviously, this collection isn’t the one that will change often. So, for this class, it will be nice to use a cache. Let’s use EhCache library for our example. So you should specify the following code in a DataSource.groovy file:

hibernate {
   cache.use_second_level.cache = true
   cache.use_query_cache = true
   cache.provider.class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}

To make a request to the Country collection, use cache, specify this in the domain class:

static mapping = {
   cache: "read-only"
}

“read-only” value should be used only for the static data; use “read-write” value. Settings of the EhCache library are placed in the grails-app/conf/ehcache.xml file. Here is a common example of configuring a cache for a domain class:

<cache
   name="com.sysgears.country"  //class to cache
   maxElementsInMemory="1000"
   eternal="false"
   timeToIdleSeconds="3600"
   timeToLiveSeconds="3600" // limit time of caching
   overflowToDisk="false"
/>

Now let’s proceed to writing a request.

int result = 0
List<Country> countries = Country.findAllByRegion(region)
Car.findAll().each { Car car ->
   countries.each {
       if (car.country.equals(it) &&
           (car.productionDate > startDate &&
               car.productionDate < endDate)) {
               result++
               return
       }
   }  
}

Now this is a first implementation that may come to your mind. But it may extremely slow down the work of our service. On my computer (Intel Core i3-2100 processor, 8GB RAM), processing this request took about 12 ms in conditions where the Cars collection has 44 records, and the Country collection has 66 records. Notice that this solution wastes memory on creating and processing unnecessary objects.

The main goal of writing the requests is to perform as many operations as possible within the database. So let’s rewrite the request using the criteria.

List<ObjectId> countries = Country.findAllByRegion(region).id
int result = Car.createCriteria().count {
   'in'('country', countries)
   between('productionDate', startDate, endDate)
}

Now this request is much more effective than the previous one, the time of processing speaks for itself, it takes about 1-2 ms to process the request. So building smart requests using criteria is a great way to increase Grails application productivity.

Software Developer