We run a Grails application with Spring Security. Our integration tests are slow: 5m34s for 414 tests including about 45secs app boot time. That's about 1,4 test per second. Not that great. These tests are mainly functional tests, using GEB. I suspected the overhead of the browser automation would be the culprit, but when it comes to performance, measure, don't guess.

I started writing well scoped tests: one that does nothing, one that hit a dummy controller that returns ok, one that creates a Company and cleans it afterwards, one that creates a Company and an Employee - a pattern we use extensively in our tests.

It turned out the Employee creation was severely impacting the performance. I suspected some GORM inheritance issues (Employee extends User), or some missing index. Again, I knew better than guessing. None of them were actually at fault. Indexes have no impact when you create then delete a single user. And there was nothing foul with the inevitable outer join.

I narrowed to some code generated by Spring Security:

def beforeInsert() {
    encodePassword()
}

By commenting out the encodePassword(), I noticed a tenfold speed improvement. That was it. The password is encoded using bcrypt and it is inherently slow to hash (I would almost say: it is slow by design). So how do you bypass the password encoding during tests? A first messy attempt to add a transient flag to User did not pan out, and instead, a colleague pointed out the S2 password parameters.

By default, S2 uses 10 rekeying rounds and 1000 hashes. It does not allow to go below a certain threshold, so we settled for this:

application.groovy

if (Environment.current == Environment.TEST) {
    grails.plugin.springsecurity.password.bcrypt.logrounds = 4
    grails.plugin.springsecurity.password.hash.iterations = 1
}

There, we now have reasonably fast tests without fiddling with the User definition.

When our Employee was under the spot, my colleague fired XRebel and ran an API operation involving employee creation. He noticed that the password encoding operations took about of the third of the time:

On top of that, we added 2 containers to our Circle CI build, moved to the latest version and tweaked the build scenarios, cutting build and deployment time almost by three.

Nice teamwork with Eric Darchis and David Nguyen at Adessa Software. Improvement is a continuous effort.


comments powered by Disqus