Hibernating

Migrating my application from Hibernate to Rails has taken longer than I expected. An important client has expressed performance concerns with their release, so I spent the last week looking for ways to tweak the current Hibernate implementation. It has been very frustrating.

I don't like to cast blame or plead ignorance, but prior to this exercise, my experience with Hibernate has been limited. The module containing our Hibernate calls was authored by someone else and I've been mostly a client of this module. It wasn't until I looked at our query log in detail did I notice how poorly this module was written and how confusing Hibernate is.

When seeing the large amount of queries in the log (often for data not needed in the current view), my first thought was, "we must have a problem in our models... something is causing Hibernate to eagerly fetch related data we have no immediate need for."

I went through our models and, indeed, I did find some simple changes where "set" declarations did not have the "lazy" flag set to true.

I had to dig a little deeper to uncover our "many-to-one" relationship problems. Hibernate's "many-to-one" declaration does not allow for a "lazy" parameter... at least not where the relationship is declared. Instead, the "lazy" option needs to be declared in the "class" declaration of the related object?!?! That seems a little backward to me. Confusing things more, declaring "outer-join" in the "many-to-one" relationship does not tell Hibernate to use an "OUTER JOIN" instead of an "INNER JOIN". No, instead it directs Hibernate to ignore the "lazy" class declaration and eagerly fetch the "many-to-one" relationship!?!? For some reason, we actually had this in our model definition.

The initial performance gain I saw was unbelievable! The application had never run so fast. This was despite the fact we have some structural problems with our database (missing indexes, non-normalized data, etc.), causing full table scans for some queries.

The world was right again... until one page access caused the dreaded "org.hibernate.LazyInitializationException: failed to lazily initialize a collection - no session or session was closed". We needed data for one of the relationships that I had set to "lazy".

We had seen this error before, and thought we had licked it. We created a ServletRequestListener that opened and closed the Hibernate session for the request. It seemed to fix the problems we were having at that time.

I started a long session of debugging and it looks as if the shared session is being used for all requests. Also interesting is that, while I changed a number of relationships to lazy, only a single one is failing.

So far, I have not been able figure out what is causing the exception for this particular relationship. The original author of this code doesn't have a clue what the problem can be. My only guess is that there is some code in the Hibernate tree attempting to access this nested data using a different session. I'm going to continue to scour the Internet for any clues.

Comments

Vinnie said…
I've posted here about how caching can complicate things. If it's not clear that caching is taking place, debugging is a nightmare.

That's exactly what happened here, and is the reason for my LazyInstantiation problem. The DB-access code was caching information (and eagerly loaded associations) at start up even though this information can change over time. Further, the author of this code never implemented a system to refresh or invalidate the cache!

I never would have expected this DB-access layer to cache any information at all. The information (and the schema itself) can change by request making any application layer cache at this layer almost useless.

It does not make any sense to me why someone would spend the time to implement this code. And it's a big reason I hate these types of "enhancements".

Popular posts from this blog

I Believe...

More stuff to get excited about

A few quick notes