I'm sitting in at Michael Nygard's talk at JAOO about handling failure in production systems. He wrote 'Release It!' a pattern book that describes common anti-patterns and illustrates how they can cause massive downtimes and lost business. During his talk he called out ORM Tools to be bad at handling some of these which urged me to make this blog.
Unbounded result set
He mentioned ORM Tools are bad at handling the pattern he calls Unbounded result set
which is done by applications assuming that a query will only return a few records but when put into production the query suddenly starts returning million of rows instead of those few that were in the development/test systems.
I asked Michael what he actually meant by this since Hibernate have always had pagniation support allowing you to safely iterate over results in a controlled manner:
Query q = sess.createQuery("from OrderLines lines where lines.order = :id") .setProperties(order) .setFirstResult(20).setMaxResults(10) List lines = q.list();
It turns out what Michael was referring to (as I expected) was that developers instead of doing focused queries maps where mapping their complete object model blindly and was doing this instead:
List lines = order.getOrderLines();
which probably works fine in 99% of the cases because there seldom are more than a few records in an Order, but sometimes you got an enterprise making a big order and you can end up with thousands line items which can have a ripple effect on the memory and cpu usage when each orderline and its connected objects starts to be fetched/used.
In my mind that is not really the fault of the ORM Tool, but more that Java's collections does not have the API to provide a transparent iteration over large collections. But saying that does not make the problem go away, some ORM Tools solves this with vendor specific collection's - in Hibernate we have Collection filters:
Collection lines = session.createFilter( order.getOrderLines(), "") .setFirstResult(20).setMaxResults(10) .list();
This gives you the exact same pagniation features on mapped collections as normal queries and helps you avoid the Unbound Result Set
anti-pattern.
Timeouts
Another issue Michael urged developers to take care of is to always use timeouts for any remote calls, incl. database queries.
Timeouts are important to use when you are accessing external systems which might become overloaded and start having slow response times or maybe even never respond.
Hibernate have support for Transaction time outs:
try { //set transaction timeout to 3 seconds sess.getTransaction().setTimeout(3); sess.getTransaction().begin(); // do some work ... sess.getTransaction().commit() } catch (RuntimeException e) { sess.getTransaction().rollback(); throw e; // or display error message } finally { sess.close(); }
and Query timeouts.
Query q = sess.createQuery("from OrderLines lines where lines.order = :id") .setProperties(order) .setFirstResult(20).setMaxResults(10) .setTimeOut(3); List lines = q.list();
which can also be used on the Collection filters:
Collection lines = session.createFilter( order.getOrderLines(), "") .setFirstResult(20).setMaxResults(10) .setTimeout(3) .list();
Hibernate does not do anything magically here we just delegate the timeout to the underlying systems thus the functionality are dependent on your database or application server vendors implementing the standard timeout functionalities.
I hope Michael will be glad to know (I know he knows ;) that if you by accident repeats some of these anti-patterns Hibernate does have the features to allow you to avoid them.