There are a lot of Timeout/session not being released issues when using Panache-reactive with smallrye-reactive-messaging in quarkus. A slight variant of the timeout issue is observed when using the PanacheRepository.
Based on the knowledge from
Panache reactiveTransactional timeout with no stack trace, I have used directly the session factory and created a Mutiny.Session and run my query and to the most part it will works except when I try to add setLockMode(LockMode.OPTIMISTIC_FORCE_INCREMENT)
as it will cause
Caused by: java.lang.UnsupportedOperationException
at org.hibernate.reactive.event.impl.DefaultReactiveLockEventListener.onLock(DefaultReactiveLockEventListener.java:240)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
at org.hibernate.internal.SessionImpl.fireLock(SessionImpl.java:727)
at org.hibernate.internal.SessionImpl.fireLock(SessionImpl.java:717)
at org.hibernate.internal.SessionImpl.access$1000(SessionImpl.java:202)
at org.hibernate.internal.SessionImpl$LockRequestImpl.lock(SessionImpl.java:2656)
at org.hibernate.loader.custom.CustomLoader$1.afterLoad(CustomLoader.java:362)
at org.hibernate.reactive.loader.ReactiveLoaderBasedResultSetProcessor.lambda$reactiveInitializeEntitiesAndCollections$3(ReactiveLoaderBasedResultSetProcessor.java:188)
at java.base/java.util.concurrent.CompletableFuture.uniAcceptNow(CompletableFuture.java:757)
at java.base/java.util.concurrent.CompletableFuture.uniAcceptStage(CompletableFuture.java:735)
at java.base/java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:2182)
at java.base/java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:144)
at org.hibernate.reactive.loader.ReactiveLoaderBasedResultSetProcessor.reactiveInitializeEntitiesAndCollections(ReactiveLoaderBasedResultSesor.java:150)
at org.hibernate.reactive.loader.ReactiveLoaderBasedResultSetProcessor.reactiveExtractResults(ReactiveLoaderBasedResultSetProcessor.java:83)
at org.hibernate.reactive.loader.ReactiveLoader.reactiveProcessResultSet(ReactiveLoader.java:145)
at org.hibernate.reactive.loader.ReactiveLoader.lambda$doReactiveQueryAndInitializeNonLazyCollections$0(ReactiveLoader.java:77)
at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
... 63 more
Ideally it should have DefaultReactiveLockEventListener.reativeOnLock
but from stack trace it went to DefaultReactiveLockEventListener.OnLock
This is my full implementation.
return sessionfactory.withTransaction((session, tx) -> {
return session.createNativeQuery("select * from model", Model.class)
.setLockMode(LockMode.OPTIMISTIC_FORCE_INCREMENT).getResultList().onItemOrFailure()
.transform((existingModels, t) -> {
if (t != null) {
Log.error(t, t);
}
List<Model> toSave = Lists.newArrayList();
for (Model publish : publishedModels) {
boolean exist = false;
for (Model existing : existingModels) {
if (publish.getName().equals(existing.getName()) {
toSave.add(existing);
exist = true;
}
}
if (!exist) {
publish.setId(UUID.randomUUID());
publish.setRevision(1L);
toSave.add(publish);
}
}
Wrap wrap = new Wrap();
wrap.setSession(session);
wrap.setModels(toSave);
return wrap;
}).chain(wrap -> {
return wrap.getSession().persistAll(wrap.getModels().toArray()).invoke(v -> session.flush());
});
});
Is this a bug or this is something that I can improve on my code to circumvent this? I needed the Lock to make sure that no two transactions that will update the same Version(Using the JPA @Version) of the record.
This following code seems to work but I am not sure if this is the right way to go.
for (Model existing : existingModels) {
if (publish.getName().equals(existing.getName()) {
toSave.add(existing);
session.lock(existing, LockMode.PESSIMISTIC_FORCE_INCREMENT);
exist = true;
}
}