This is critical for us, it seems Envers is corrupting our _AUD audit tables with extra records of REVTYPE=1 which are missing the previous REVEND (we are using the validityAuditStrategy) references.
mysql> select Id, REV, REVTYPE, REVEND, Day, Hour, Change_Origin_Date_and_Time_Id from Date_and_Time_AUD where Id=36076240;
| Id | REV | REVTYPE | REVEND | Day | Hour | Change_Origin_Date_and_Time_Id |
| 36076240 | 244111 | 0 | 244135 | NULL | NULL | NULL |
| 36076240 | 244128 | 1 | NULL | NULL | NULL | NULL |
| 36076240 | 244135 | 1 | NULL | NULL | NULL | NULL |
3 rows in set (0.00 sec)
Our users are then getting errors when updating records because an AssertionFailure occurs:
2011-09-04 15:48:45,564 ERROR [http-80-45] AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
java.lang.RuntimeException: Cannot find previous revision for entity com.mycompany.i18n.DateAndTime_AUD and id 36076240
We are using Hibernate (and Envers) v3.6.5. Everything seemed to be working fine before, we suddenly started receiving tons and tons of these errors and user complaints. I cannot reproduce this locally and the error doesn't ALWAYS (though more and more frequently) happen in production. I've restarted production, redeployed, etc. to no avail. This ONLY happens on our DateAndTime class for some reason, which is not much different from the MANY other hibernate managed entities which are frequently created and updated in production. After the above error occurs (and the user is sufficiently annoyed), a new revision is created kind of correcting the problem where the first revision's REVEND now refers to the new revision (skipping the corrupted revision).
I'd add that there are concurrent transactions, and it seems that both are started in a same time, but second transaction overwrites result of first one instead of add REVEND value on first update row. Database is MySQL 5.5. I'll try to add a test case for this situation. If anybody have some advice about how to avoid these kind of errors, please share your thoughts.
Just read your message. Yes, issue is reproduced by test. I have 30 concurrent transactions and only one valid last revision record as a result (on MySQL, other databases works). I tried also MyISAM engine (it's not an option for us, but I was just interested to run it), and I had about 10-14 invalid revisions, other just failed to find last correct revision.
I've looked into ValidityAuditStrategy source code, and it seems to me, I found a cause of this issue. MySQL does not lock record from reading during transaction. But this strategy first select last revision record, and then updates it. So, when there are concurrent transactions, they all get the same revision record to update, so they updates the same record. If using just sql like UPDATE TABLE SET REVEND=... WHERE ID=... AND REVEND IS NULL, all works, as table is locked by the first transaction, and others transactions wait for lock to release.
Just pushed to my test slighty changed ValidityAuditStrategy class. Difference:
- List<Object> l =qb.toQuery(session).list()
+ List<Object> l =qb.toQuery(session).setLockOptions(LockOptions.UPGRADE).list();
as a result, Hibernate issues SELECT FOR UPDATE statement which locks last revision record from concurrent transactions. How do you think, can you incorporate this patch to Envers?
For your convenience I made a fork of hibernate and added this change there. You can find diff at https://github.com/rsvato/hibernate-core/commit/ae8a8310e4cbcabbacb7e01259d3025378be9edb
Here are the links to the fixes you made in 3.6 and 4: