I like to refer to this issue as 'repeatable finder' problem because it it in some sense opposite to 'non repeatable read'. Because hibernate reuses objects attached to its session, results from a finder may include some old versions of objects which are now stale.
The problem is technically a Hibernate design gotcha, but since Hibernate session is implicit in Grails and Grails domain objects are long lived (HTTP request is long to me) I decided to ask this question in the context of Grails/GORM.
I would like to ask experts here if there are any commonly established strategies for dealing with this issue.
Consider this:
class BankAccount {
String name
Float amount
static constraints = {
name unique: true
}
}
and 'componentA' code:
BankAccount.findByName('a1')
'componentB code:
def result = BankAccount.findAll()
Assume that componentA executes first, followed by some other logic, followed by componentB, result from component B is rendered by a view. Components A and B do not want to know much about each other.
This way componentB result contains old version of BankAccount 'a1'.
Many very embarrassing things can happen. If BankAccounts have been concurrently modified the presented list can, for example, contain 2 items with name 'a1' (uniqueness looks gone to the user!) or money transfers between accounts can show as partially applied transaction (if money was transferred from a2 to a1 then it will show deducted from a2 but not there yet for a1). These problems are embarrassing and can reduce user confidence in the application.
(ADDED 9/24/2014: Here is an eye opening example, this assert may fail:
BankAccount.findAllByName('a1').every{ it.name == 'a1' }
Examples of how that happens can be found in any of the linked JIRA tickets or my blog. )
(ADDED 9/24/2014: NOTE: a seemingly sound advice to use database enforced unique keys in implementing equals() method is not concurrency safe. You may get 2 object with the same value of the 'business key' which are different.)
Possible solutions seem to be add a lot of discard() calls or a lot of withNewSession() calls and deal with LazyIntializationExeption and DuplicateKeyException, etc.
But if I do that why am I using hibernate/GORM? Calling refresh on each object returned from each query seems simply ridiculous.
My current thinking is that using short sessions/withNewSession in certain critical areas are the best approach but it does not solve the issue in all cases, just is some critical application areas.
Is this something Grails applications have to live with? Can you point me to any documentation/discussion about this issue?
EDITED 9/24/2014: Relevant Grails JIRA ticket: https://jira.grails.org/browse/GRAILS-11645, Hibernate JIRA: https://hibernate.atlassian.net/browse/HHH-9367 (has sadly been rejected), my blog has more detailed examples: http://rpeszek.blogspot.com/2014/08/i-dont-like-hibernategrails-part-2.html
ADDED 10/17/2014: I got several replies stating that this is any DB application/any ORM issue. This not correct.
It is true that this problem can be avoided by using long transactions (Hibernate session length/HTTP request length) + setting higher than normal DB isolation level of REPEATABLE READ. This solution is simply not acceptable (why do we have transnational services if for the application to work properly we need HTTP request long transactions!?)
DB applications and other ORMs will not exhibit this problem. They will not need long transactions to work and the problem is prevent with just READ COMMITTED.
It is now 2 months, since I posted this question here, and it has not received a meaningful answer. That is simply because this issue has no answer. It is something that Hibernate can fix, not something a Grails application can fix.ADDED 10/17/2014-END