NonBatchingBatch does not abortBatch() after StaleStateException

Description

When attempting to update the same entity with two threads, the entity is retrieved from the DB in both threads. First thread completes the update, but the second thread gets a StaleStateException Here are the steps which Hibernate performs to update the entity in the second thread, starting from the point when it attempts to persist:

1. A PreparedStatement is constructed.
2. PreparedStatement is executed using the NonBatchingBatch.
3. StaleStateException is thrown by the second thread.
4. PreparedStatement is closed.
5. Transaction is rolled back.
6. I attempt to retry the update...
7. Entity is re-fetched from the DB.
8. Entity fields are updated.
9. Same session attempts to persist the entity to the DB.
10. NonBatchingBatch is still present in the JdbcCoordinator
12. AbstractEntityPersister#update:3049 gets the same NonBatchingBatch
13. AbstractEntityPersister attempts to execute previously closed PreparedStatement
14. java.sql.SQLException: Invalid state, the PreparedStatement object is closed.

Environment

None

Activity

Show:
Steve Ebersole
October 22, 2018, 5:40 PM

Unfortunately, maybe we should. The "problem" is the org.hibernate.jdbc.Expectation contract as currently defined. It explicitly lists SQLException and HibernateException as possible exceptions. Ideally I would have limited Expectation to throwing JDBCException or StaleStateException.

Steve Ebersole
October 22, 2018, 5:47 PM

FWIW, BatchingBatch handles any RuntimeException (in addition to SQLException). That seems sketchy, but not sure why I did that there.

Guillaume Smet
October 23, 2018, 9:29 AM

any chance you could come up with a test case?

We have a test case template here: https://github.com/hibernate/hibernate-test-case-templates/tree/master/orm/hibernate-orm-5 .

Sean Crouse
October 24, 2018, 3:41 PM

I haven't found the time to come up with a test case, but I should be able to in the next few days.

Sean Crouse
November 12, 2018, 9:51 PM
Edited

While writing a unit test for 5.3.6, I was unable to reproduce the bug. The reason is AbstractEntityPersister line 3270. In 5.1.0, it doesn't compare the jdbcBatchSizeToUse, and useBatch is true even if jdbcBatchSizeToUse == 1. This leads to lines 3291-3294 being executed. In 5.3.6, it only uses a batch when jdbcBatchSizeToUse > 1, but then BatchBuilder lines 69-71 never return a NonBatchingBatch.

Unless a new class is written in the future, which extends AbstractEntityPersister, and overrides the update method, I don't see BatchBuilder ever returning a NonBatchingBatch.

Assignee

Unassigned

Reporter

Sean Crouse

Fix versions

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Feedback Requested

2018/10/22

Feedback Requested By

Steve Ebersole

Affects versions

Priority

Major
Configure