Software has two ingredients: opinions and logic (=programming). The second ingredient is rare and is typically replaced by the first.
I blog about code correctness, maintainability, testability, and functional programming.
This blog does not represent views or opinions of my employer.

Friday, October 3, 2014

I don't like Hibernate/Grails, part 8, but some like Hibernate and Grails. Why?

Small change in plans. I wanted to write about testing, but this topic logically precedes testing. Writing last 2 posts made me realize something: each application is different and Grails/Hibernate problems I am likely to notice are very, very much dependent on the app I am working on.

Simple App:  Think of something you can generate by asking Grails to do the coding for you. Simple app is a CRUD application with these characteristics:
  • Simple domain Objects without relationships
  • No transactions/services
  • Not more than one hibernate query per request
  • Simple validation logic (contained in domain objects)
This example repeats part of my last post.  Assume that I have a domain object User which looks similar to this:
    class User {
        ...
        Office office
        UserPreferences preferences
    }

In a simple app this code:
    User.findAllByOffice(office1) //Code Example (A)

will exhibit only one type of side-effects: it issues SELECT statement + some DB locks.

Complex App:  In my last post I have listed several side effects that can be associated with (A). Clearly, complex apps are a different ball game!  Here are the side effects listed again.  Code (A)
  1. will issue a SELECT statement (with whatever locks) 
  2. can save some changed objects to the database (part 5)
  3. can save some unchanged objects too  (part 6)
  4. will impact some record types returned by queries that follow it (from proxies to actual objects).  Some records returned from this query will use Hibernate Proxy to implement preferences and some will use the actual UserPreferences class. (Some developers may be surprised that the same goes for the office association). (part 4)
  5. will impact the data content of some records returned by queries that follow it.  Similarly, the content of some records returned from this query may be different from the data returned by the underlying SELECT statement. (part 2)
Side-effect 2-5 have very non-local impacts (can affect unrelated parts of code sharing the same hibernate session) and can be extremely hard to avoid.  This leads to code that is unpredictable.  So, how is it possible for Hibernate to be so popular?

Counting the number of side-effects in (A) that impact my code is just one possible measure of my app complexity.  As my application becomes more complex, these side-effects become 'louder' (impacts become more frequent and more noticeable).  In this post, I am less interested in how many side-effects impact my code, more in how 'loud' they are. 

Following table shows a fictional CRUD application that started very simple and became more complex over time.  
H5 - total number of side-effects in queries similar to (A)
H6 - frequency of problems related to queries similar to (A)



Complexity
Exposure to Problems
Explanation of impacts
H5
H6
1
Simple App
Happy Days!
Happy Days!
1

2
Add Domain Object Relationships
Hibernate Proxy Objects
2
low
3
Add more complex validation - more than one query in the same session
Repeatable Finder
3
low
4
Move validation logic outside of domain objects
Auto-flushing
4
low -mid
5
Add Services and Transactions
LazyInitializationException

Auto-flashing becomes less loud (unwanted saves are rolled back)

Transactional integration tests diverge from reality
Auto-flushing can still be a problem: saving can fail.
mid
6
More complex data passed from client
Unmarshalling of client data keeps changing between Grails versions
Small intro to version upgrade problems

mid
7
Added logic in Filters
Higher probability of repeatable finder issue.

Need for more complex test infrastructure

Side Note: What is the before-after-afterView ordering if Controller does a forward?

Potentially interesting filter cleanup or setup ordering issues

mid
8
Application managed hibernate sessions
DuplicateKeyException and similar hard to troubleshoot errors.

Queries can save unmodified objects.
5
high

8 needs more explanation.  Here is one example why I need to create small hibernate sessions:
Integration tests may need to include scenarios mimicking activity performed over several HTTP requests.  Typically, I see such tests mashing all logic into one test method executing everything in the scope of the same hibernate session.  In real life, each of the requests will be performed in a separate hibernate session. Tests have diverged from reality.  Side-effects 2-5 listed above will have dramatically different impact on the code when ran in the test environment.

Audacious App:
Consider Grails/Hibernate implementing Type 2 Slowly Changing Dimension (the one using time slices).  To do that, I may want to use something like a session variable (available in most databases, including Oracle or Postgresql) to define a time point and use read-only views to get a snapshot of all my data at the specified time point.  I will configure my GORM domain objects against these views and see what happens.

To use session variables, I will need to wrestle with Spring framework DB connection management to make sure that the connection is not swapped under me, because that would change the definition of session variable.  That is not trivial but doable.

How does that change my exposure to Hibernate side-effects?  Consider this: moving the time-point one day forward applies a day worth of user activity in just a few milliseconds!  Many concurrency problems in the application just became super loud. That includes repeatable finder (side-effect  5, my blog part 2).  Because of that I need to wrap each use of a time-point session variable with a withNewSession() block (or something equivalent).  That exposes my code to issues documented in part 3 and 6 (side-effect number 3!).  All of these are now super loud.

Conclusions:
This is my best attempt to explain the discrepancy in perception of Hibernate and Grails. I think there is more going on that I do not understand, but this is my best answer to-date.

The 5 side-effects listed in this post are worth investigating more.  I will refer to them again when talking about testing (next post).

3 comments:

  1. Looking forward to the upcoming posts, this series has been entertaining and thorough so far :)

    ReplyDelete
  2. very-very interesting topic - wishing to have more traffic here. My experience: we are at version n++ of a large Grails application (~300 tables) and we hit the wall constantly with Hibernate/Grails. We have an unconditional love for Grails - but for us the only way to get things running is to use groovy.Sql. Will contribute here with some examples asap. Julio.

    ReplyDelete
  3. I feel very grateful that I read this. It is very helpful and very informative and I really learned a lot from it.

    ReplyDelete