| When using `find` to SELECT - FOR UPDATE NOWAIT, no exception is ever returned to the blocked thread, and it hangs. The original test was in Spring Data JPA (see https://jira.spring.io/browse/DATAJPA-1259): I've tried my best to paraphase the test into straight Hibernate, but I don't have access to the tools here so there might be some bugs.
entityManager.find(ActivationLock.class, key, LockModeType.PESSIMISTIC_WRITE, Collections.singletonMap("javax.persistence.lock.timeout", 0));
where the full test is
@Entity
@Table(name = "CONF_ACTIVATION_LOCK")
public class ActivationLock {
@EmbeddedId
ActivationLockKey key;
... Getters, setters, equals
}
@Embeddable
public class ActivationLockKey implements Serializable {
private Long configId;
@Convert(converter = NetworkConverter.class) private NetworkEnum network
... Getters, setters, equals...
}
@Component // Config is on the abstract test, enables transaction management and scans this package for components public class ActivationLockTest extends AbstractOracleDrivenUnitTests { @PersistenceContext EntityManager underTest; @Test public void testFindAll() throws Exception { ActivationLockKey key = new ActivationLockKey(42L, NetworkEnum.PRODUCTION); ActivationLock requested = new ActivationLock(key); if(!underTest.exists(key)) { initRow(requested); } AtomicBoolean ready = new AtomicBoolean(false); AtomicBoolean contended = new AtomicBoolean(false); AtomicBoolean done = new AtomicBoolean(false); new Thread(() -> lockRows(requested, ready, contended, done)).start(); new Thread(() -> contendRows(requested, ready, contended)).start(); Awaitility.await().atMost(20, TimeUnit.SECONDS).until(done::get); } @Transactional public void initRow(ActivationLock requested) { ActivationLock created = underTest.persist(requested.getKey()); assertEquals(created, requested); } @Transactional public void contendRows(ActivationLock requested, AtomicBoolean ready, AtomicBoolean contended) { Awaitility.await().until(ready::get); try { List<ActivationLock> lockedRows = underTest.find(ActivationLock.class, requested.getKey(), LockModeType.PESSIMISTIC_WRITE, Collections.singletonMap("javax.persistence.lock.timeout", 0)); //This thread gets stuck here //Should have thrown an exception by now assertNull(lockedRows); // Did we just get null instead of an exception? fail(); } catch (Exception e) { System.out.println("Failed"); //Good! What did we get? e.printStackTrace(); } contended.set(true); } @Transactional public void lockRows(ActivationLock requested, AtomicBoolean ready, AtomicBoolean contended, AtomicBoolean done) { List<ActivationLock> lockedRows = underTest.find(ActivationLock.class, requested.getKey(), LockModeType.PESSIMISTIC_WRITE, Collections.singletonMap("javax.persistence.lock.timeout", 0)); assertEquals(lockedRows.get(0), requested); ready.set(true); Awaitility.await().until(contended::get); //This thread waits forever here done.set(true); } } The code issues the SQL I would expect, I see this SQL twice in the logs Hibernate: select CONFIG_ID from CONF_ACTIVATION_LOCK where CONFIG_ID =? and NETWORK =? for update nowait I would except some kind of exception, but in fact we just deadlock where I marked, and Awaitility kills the test after the timeout. Please let me know if you need more details! |