Grails: Beware of the second level cache

Know your caches!

Recently we were hunting a strange bug. Take the following domain model:

class Computer {
  Coder coder
}

class Coder {
  static hasMany = [projects:Project]
}

Querying the computer and iterating over the respective coder and projects sometimes resulted in strange number of projects: 1. Looking into the underlying database we quickly found out that the number of 1 was not correct. It got even more strange: getting the coder in question via Coder.get in the loop yielded the correct results. What was the problem?
After some code reading and debugging another query which was called after the first one but before accessing the coder in the loop gave some insight:

  Coder.withCriteria {
    projects {
      idEq(projectId)
    }
  }

This second query also queried the Coder but constrained the projects to a specific one. These coders were populated into the second level cache and when we called computer.coder the second level cache returned the before queried coder. But this coder had only one project!
Since we only needed the number of coders with this project we changed the second
query to using count, so no instances of Coder are returned and thus saved in the second level cache. Bug fixed.

3 thoughts on “Grails: Beware of the second level cache”

  1. I’m having somethiing similar happen. I keep getting stale data back.
    What do you mean by ” we changed the second
    query to using count, so no instances of Coder are returned and thus saved in the second level cache. Bug fixed.” ?
    Can you please give an example ?

    1. The problem here is all instances returned by the query are stored in the second level cache. But if your query does not return instances but scalars the y are not stored.
      For example:

      Coder.withCriteria {
          projects {
            idEq(projectId)
          }
        }
      

      returns instances of Coder
      but

      Coder.withCriteria {
          projects {
            idEq(projectId)
          }
        projections {
          count('id')
        }
      }
      

      returns an int. You could even use projections { property(“id”) } or another property.

  2. Great – thanks !
    Actually – I just got it working by clearing the session :
    sessionFactory.getCurrentSession().clear()

    This clears the underlying cahce

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.