-
-
Notifications
You must be signed in to change notification settings - Fork 101
Description
A user can write this:
return sessionFactory.withSession(s -> {
return Uni.combine().combine().all().unis(s.doSomething(), s.doSomethingElse()).asTuple();
});
It is incorrect, because it could result in interleaved uses of the session, messing up its state: doSomething()
could set up some temporary state, do some IO so release control of the event loop, then doSomethingElse()
would start executing, mess up the temporary state, and we're in a bad place.
This section of the docs explains a related problem -- not the same one, but the explanations are relevant.
Unfortunately -- on contrary to the problem mentioned in the docs -- this problem is not detected and will be very hard to debug.
More importantly... it's probably just as easy to fix as it is to detect. We could simply make sure that all operations in the session start running after the previous one finished.
E.g. this code in ReactiveSessionImpl
:
@Override
public <T> CompletionStage<T> reactiveFind(
Class<T> entityClass,
Object id,
LockOptions lockOptions,
EntityGraph<T> fetchGraph) {
checkOpen();
return supplyStage( () -> {
...
} );
Could become:
@Override
public <T> CompletionStage<T> reactiveFind(
Class<T> entityClass,
Object id,
LockOptions lockOptions,
EntityGraph<T> fetchGraph) {
checkOpen();
this.previousOperation = this.previousOperation.thenCompose(ignored -> supplyStage( () -> {
...
} ) );
return previousOperation;
(Or better yet, we'd integrate this with supplyStage
, which seems to be doing something similar with a voidFuture
)
previousOperation
would just be a field in the session, initialized to a completed future.
We could do this for StatelessSession
as well, though it will be harder to explain why state can be a problem with a "stateless" session...