Memcached in Grails: a quickie

Grails, programming

Two clients will be covered: XMemcached and spymemcached

Xmemcached

Add dependency

Add in BuildConfig.groovy:

dependencies {
...
compile 'com.googlecode.xmemcached:xmemcached:1.4.2'
}

Set up the bean

Add in conf/spring/resources.groovy:

beans = {
...
memcachedClient(net.rubyeye.xmemcached.utils.XMemcachedClientFactoryBean) {
servers="localhost:11211"
}
}

Usage in a service

class MyService {
static final int expiry = 30 // 30s

def memcachedClient

def perform() {
def val = memcachedClient.get('key')
if (!val) {
// cache miss
...
// put in cache
memcachedClient.set('key', expiry, val)
}
// use val
}
...
}

Spy Memcached

Add dependency

Add in BuildConfig.groovy:

dependencies {
...
compile 'net.spy:spymemcached:2.9.1'
}

Set up the bean

Add in conf/spring/resources.groovy:

beans = {
...
memcachedClient(net.spy.memcached.spring.MemcachedClientFactoryBean) {
servers="localhost:11211"
}
}

Usage in a service


class MyService {
static final int expiry = 30 // 30s

def memcachedClient

def perform() {
def val = memcachedClient.get('key')
if (!val) {
// cache miss
...
// put in cache
memcachedClient.set('key', expiry, val)
}
// use val
}
...
}

References

Notes on multiple Grails apps sharing a DB

Grails, programming

Multiple Apps

Two logical partitions of services, perhaps one public-facing with GSPs serving up UI and one headless with REST services, can be easier to maintain if implemented as separate Grails apps.

If there is a breaking change in a domain (and corresponding tables) in one app, for instance, it can be re-deployed without disrupting the other app as long as that other app doesn’t use the domain in question.

Shared domains in in-place plugins

On the other hand, there may be domains that are relevant and used by both apps. One way to make this work is to put them in a plug-in, referenced by both apps. This plug-in can even be an in-place plug-in if the iteration of editing and publishing the plug-in proves tedious during development.

The trouble with in-place plugins

Various tools support an evolving in-place plug-in with varying success (Groovy/Grails Tool Suite 3.3.0, for instance, doesn’t even seem to recognize them). IntelliJ IDEA 12.1 Ultimate (the paid version) seems to recognize them and handles them fine most of the time … because even Grails itself seems flaky at times, requiring periodic cleaning up of cached binaries of apps that use in-place plugins:

rm -rf ~/.grails/.slcache
rm -rf ~/.grails/2.2.3/projects/myApp

database-migration

After about a week of Grails development, most people will realize that they’ve outgrown that dataSource’s dbCreate="create" option. And for a while after that point, perhaps dbCreate="update" may buy some more time. Beyond that, some real SQL migration tool is needed.

Rails has “rake db:migrate“, and Grails has “grails dbm-update” with the database-migration plugin (currently 1.3.5).

Unfortunately, while the database-migration plugin works great for a single Grails app connecting to an SQL DB, it is very cumbersome to get working for multiple apps sharing an SQL DB.

Disjoint domains

If app1 has domain A and app2 has domain B, and the shared DB naturally has tables a and b, running dbm-gorm-diff in app1 will give you a changeset that will attempt to drop table b. Similarly, running dbm-gorm-diff in app2 will give you a changeset that will attempt to drop table a. This is because app1 doesn’t know about domain B and therefore table b. The dbm-gorm-diff tool will assume that domain B has been deleted, so it dutifully adds an entry to clean up table b for you. And similarly, running dbm-gorm-diff in app2 will add an entry to drop table a.

One way I found to get around this is to add app2’s tables (including joining tables and sequences thereof) to app1’s grails.plugin.databasemigration.ignoredObjects and app1’s tables and friends into app2’s grails.plugin.databasemigration.ignoredObjects.

But that’s pretty tedious work and a maintenance pain.

Shared domains

Then there are the placements of these changelog files. The dbm-* tools look in only one place for them: grails-app/migration. So that means there are N lists of these files if I have N apps sharing a DB. So in the least there is a duplicate of the base/root changelog file in each app’s grails-app/migrations/. Then each app has its own changelog files for changes to its own domains.

What about shared domains? Those from the in-place plugin? Where do changelogs for those domains go? To avoid problems, of course, they need to go into all the apps (not strictly necessary, but for consistency and documentation, better to replicate these), just like the base/root changelog.

So MORE maintenance steps.

At this point one wonders if it’s better to craft these changelogs manually. But for the most part, dbm-gorm-diff saves a lot of work. So I’m not entirely ready to let it go yet.

Policy

I presented this to my team, and they agreed that maintaining separate changelog files and ignored lists are tedious and error-prone. We ended up biting the bullet and take the one-schema strategy:

  • All domains in general should reside in the shared in-place plug-in, whether they were actually shared between apps or not
  • Pick one of the apps from which to perform database-migration tasks, and put the changelogs in that app

The result is that each app gets the union of all the domains, and each of the apps use a proper subset of that group of domains. It’s not as lean and pure, but it’s a good compromise, I believe.

So when there is a schema change, we now need to assess the impacted apps and redeploy each as needed.