Lost Update caused by optimistic lock excluded attributes

Description

We originally discovered this issue with Hibernate 4.3.10 but could reproduce it on 5.2.10 as well.
Basic description
If an attribute that is excluded from optimistic locking is updated the update may reset the value of other attributes which are not excluded from optimistic locking. For this to occur two major factors play a role. First factor is that an entity has base class with InheritanceType.JOINED which declares a version attribute for optimistic locking. The second factor is that the optimistic lock excluded attribute and the attribute suffering the lost update are both declared on the same entity inheriting from that base class.
If one transaction updates an attribute not excluded from optimistic locking with a concurrent transaction updating the attribute which is excluded from optimistic locking the value of the value of the not excluded attribute is reset if the concurrent transaction finishes later.

We tried to set org.hibernate.annotations.DynamicUpdate on the entity class which is affected as a Workaround, but this only solves part of the issue. After enabling dynamic updates on the entity class the lost update was solved, but the entity cache returned an entity with the problematic state.

Steps to reproduce

  1. unzip attached reproducer

  2. run "mvn test" in the context of the unpacked directory

Further notes
The issue seems to occur in this combination because if the version attribute were declared on the same table as the other attributes hibernate issues the update statement with a check for the version attribute. If the version attribute has changed meanwhile the second update on the lock excluded attribute would not change any data and the stale object state would be reported via an exception. With joined inheritance the check in the where clause is absent and thus the data is updated and the stale object state is not recognized.
Without optimistic lock exclusion, the second transaction would actively check the version in the table of the base class and recognize the concurrent modification.
The changed behavior after applying dynamic updates is caused by the fact that the reset of the optimistic lock included attribute results from the flush of all attrbutes independent of them having changed.

Attachments

1

Activity

Show:

Aaron Schmischke July 28, 2017 at 7:59 AM

I do not agree that flushing potentially stale state of not excluded attributes is inevitable and found no clues in the documentation for optimistic lock exclusion regarding this behavior.
The bahvior is even inconsistent as the stale object state is recognized if the version attribute is located in the same table as the optimistic lock excluded attribute.
Additionally, as mentioned in the description, if @DynamicUpdate is used the state of the not excluded attribute may diverge in the database and the entity caches. Without the cache issue this would evite the so called inevitable.

If this really is the intended behavior I would be quite grateful for a hint towards the documentation regarding this.

蔡纯 July 28, 2017 at 7:22 AM

If this "optimistic locking excluded" attribute is updated, isn't it normal that version will not be checked during commit if only this "optimistic locking excluded" attribute are updated during a transaction? That's what @OptimisticLock(excluded=true) exactly should do, and it's inevitable that the transaction will continue and other attribute will be override.

But if any other attributes(not optimistic locking excluded) has been updated at the same transaction, hibernate will check the version during the commit, and the version mismatched will be detected if concurrent transactions exist, which will raise OptimisticLockException accordingly.

Details

Assignee

Reporter

Components

Affects versions

Priority

Created July 27, 2017 at 5:58 AM
Updated July 28, 2017 at 7:59 AM