RESTEasy Reactive Blocking vs Non-Blocking by default

1,493 views
Skip to first unread message

Stuart Douglas

unread,
Jun 25, 2021, 2:11:47 AM6/25/21
to Quarkus Development mailing list
Hi Everyone,

I would like to revisit the topic of the default blocking vs non-blocking for RESTEasy Reactive. As we start to push users more towards RESTEasy Reactive I think the non-blocking by default behaviour is problematic for new users. For example if I generate an application with RESTEasy Reactive an Panache, and then attempt to use a panache entity in the endpoint I end up with:

"java.lang.IllegalStateException: You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking EntityManager operations make sure you are doing it from a worker thread."

Now we could improve this error message to try and detect what has happened and tell the user to use @Blocking, however it's still not a great user experience where the most basic of CRUD endpoints fails out of the box.

I am not really sure what the best solution is here, but I think we have a few options:

1) Change the default to @Blocking, but in a smart way so we can still dispatch to @Async endpoints without switching to a worker thread

I think it is more likely that a reactive programmer will understand the difference between worker and IO threads, so it makes more sense to make the reactive programmer have to change the default, while someone just getting started may not have a clue why they need @Blocking or what the difference is.

2) Always include an Application class with @Blocking in the generated codestart
3) Include an Application class with @Blocking in the generated codestart if any blocking extensions are selected

E.g. if you include Hibernate ORM you will end up with an app configured for blocking by default, if you only include reactive extensions you won't.

I don't super like either of these options, but in terms of making the getting started experience easy maybe we should change the default? I think leaving things as they are will potentially be a barrier to new users if we start to push RESTEasy Reactive more.

What do people think?

Stuart

Georgios Andrianakis

unread,
Jun 25, 2021, 2:16:13 AM6/25/21
to Stuart Douglas, Quarkus Development mailing list
Thanks for bringing it up Stuart,

I personally think 3 is the best solution, but 2 would also be nice.
Avoiding 1 should be our goal I think because not only would it be surprising for existing users, but it would also make it more difficult that solution 2 to get the full power of the Reactive behavior

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAD%2BL2cy0OXvaGDhj4Lf8LfvtQwvQ-ff2Aq9bT5EqQB3UU_erWg%40mail.gmail.com.

Alexey Loubyansky

unread,
Jun 25, 2021, 3:04:31 AM6/25/21
to Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
A possible argument against changing the default could be that the resteasy-reactive isn't the default JAX-RS extension. To have it in the app it (or one of the reactive extensions that depend on it) has to be added to the project by the user. "Reactive" is in its name, which should raise awareness.

Martin Kouba

unread,
Jun 25, 2021, 3:26:41 AM6/25/21
to gand...@redhat.com, Stuart Douglas, Quarkus Development mailing list
On 25. 06. 21 8:15, Georgios Andrianakis wrote:
> Thanks for bringing it up Stuart,
>
> I personally think 3 is the best solution, but 2 would also be nice.

I think that we should try to go with the option 3. But we should
probably always add some comment to the resource method to explain the
justification, e.g. "// @Blocking was added because ..." if a "blocking"
extension was added and "// Consider adding @Blocking if a blocking code
is executed in your resource method..." otherwise.
> <mailto:quarkus-dev...@googlegroups.com>.
> <https://groups.google.com/d/msgid/quarkus-dev/CAD%2BL2cy0OXvaGDhj4Lf8LfvtQwvQ-ff2Aq9bT5EqQB3UU_erWg%40mail.gmail.com?utm_medium=email&utm_source=footer>.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Quarkus Development mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to quarkus-dev...@googlegroups.com
> <mailto:quarkus-dev...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/quarkus-dev/CALeTM-kf_E%3D3bKxEycDzgZNMGYfon367%3Dkz8HWsDcE12T3nU%3DQ%40mail.gmail.com
> <https://groups.google.com/d/msgid/quarkus-dev/CALeTM-kf_E%3D3bKxEycDzgZNMGYfon367%3Dkz8HWsDcE12T3nU%3DQ%40mail.gmail.com?utm_medium=email&utm_source=footer>.

--
Martin Kouba
Software Engineer
Red Hat, Czech Republic

Loïc MATHIEU

unread,
Jun 25, 2021, 3:31:50 AM6/25/21
to Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
It's more complex than that, what will you do when a user includes the MongoDB extension which supports both blocking and reactive ?
What if a user includes Hibernate ORM and some other reactive extension, and always dispatches to a worker thread (via a subscribeOn() method) because he knows what he's doing ?


Loïc MATHIEU

unread,
Jun 25, 2021, 3:32:25 AM6/25/21
to Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
code.quarkus.io can also displays reactive extension with a picto to differentiate and suggest to using reactive counterparts ...

Alexey Loubyansky

unread,
Jun 25, 2021, 3:40:17 AM6/25/21
to Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
Sure, but how will the resteasy-reactive will end up in the project?

Guillaume Smet

unread,
Jun 25, 2021, 3:45:06 AM6/25/21
to Loïc MATHIEU, Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
The problem is not with users who know what they are doing. The problem is that most users have absolutely no idea what reactive means (and don't care about learning about it, they just want to keep doing what they are doing). If going reactive didn't have any trade-off, we could just keep our position and say "just learn this new thing" but that's not the case.

My guess is that in a reasonable future, we will want to make RESTEasy Reactive our default REST stack. IIRC, I advocated for blocking as default exactly for this reason back in the time: people used to reactive know what they are doing and can tweak things, people not aware of reactive can just use it without any hassle (and more than that, they are not even aware reactive exists and the constraints it comes with).

I suppose taking into account the extensions could be an acceptable trade-off, even if not perfect (given some extensions expose both paradigms as mentioned by Loïc). In that case, I would go the blocking route by default and go with the comment Martin asked for.

On Fri, Jun 25, 2021 at 9:31 AM Loïc MATHIEU <loik...@gmail.com> wrote:

Stephane Epardaud

unread,
Jun 25, 2021, 3:46:07 AM6/25/21
to Alexey Loubyansky, Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
There are various extensions that have both blocking and reactive variants in them, no? Not sure we can distinguish every extension as blocking/non-blocking.

Perhaps in DEV mode, we can make the exceptions include a link to the DEV UI that explains the problem with a button to "Make the application blocking/non-blocking by default" which would generate the application class annotated with @Blocking/@NonBlocking (do we even have that?).

This way HR would throw with that link when running on a worker thread, and ORM would throw with that link when running on an IO thread?




--
Stéphane Épardaud

Alexey Loubyansky

unread,
Jun 25, 2021, 3:48:56 AM6/25/21
to Stephane Epardaud, Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
Sure, the question is how is reactive part enabled? Isn't it supposed to be an explicit choice of the user?

Alexey Loubyansky

unread,
Jun 25, 2021, 3:59:55 AM6/25/21
to Guillaume Smet, Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
On Fri, Jun 25, 2021 at 9:45 AM Guillaume Smet <guillau...@gmail.com> wrote:
The problem is not with users who know what they are doing. The problem is that most users have absolutely no idea what reactive means (and don't care about learning about it, they just want to keep doing what they are doing). If going reactive didn't have any trade-off, we could just keep our position and say "just learn this new thing" but that's not the case.

If users select a reactive extension and don't know what reactive means, and don't care about learning what that means, I'd say it's their problem. IMO, the exception we are throwing in case of JPA/JDBC is of great help (even to those who know what they are doing but simply missed the @Blocking in the code).

My guess is that in a reasonable future, we will want to make RESTEasy Reactive our default REST stack. IIRC, I advocated for blocking as default exactly for this reason back in the time: people used to reactive know what they are doing and can tweak things, people not aware of reactive can just use it without any hassle (and more than that, they are not even aware reactive exists and the constraints it comes with).

If we do change to the resteasy-reactive as the default JAX-RS extension that'll change the situation. Currently, it's an explicit user's choice.

Loïc MATHIEU

unread,
Jun 25, 2021, 4:35:28 AM6/25/21
to Alexey Loubyansky, Guillaume Smet, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
> My guess is that in a reasonable future, we will want to make RESTEasy Reactive our default REST stack.

I remember some discussion about this, my understanding of this one is that the "classical" RESTEasy extension will be refactor to use RESTEasy reactive under the hood in blocking mode. Doing this will be invisible to user (as when we change the default HTTP engine from undertow to Vert.x) and will lower the number of engine we manage without impacting anyone.

Paul Carter-Brown

unread,
Jun 25, 2021, 4:49:16 AM6/25/21
to alexey.l...@redhat.com, Guillaume Smet, Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
I know it would be complex to implement but the best option IMHO is to instead of throwing an exception when blocking on an event loop thread, rather flag the rest resource method as blocking, log a warning and from that point on in the lifetime of the process, pretend the developer added @Blocking annotation on the method.

This would mean that:
a) Any example or combination of extensions would always "work" OOTB
b) If you don't block or do anything special, you get the best performance possible
c) If you end up blocking an event loop, it won't happen again on that method

Perhaps there could be a config setting for resteasy reactive for eventloop blocking policy - something like:
- fail: do what it does currently
- warn: log a warning and continue and don't adapt to blocking for subsequent calls
- auto: log a warning and adapt to blocking for subsequent calls

Then I would make it auto by default and encourage using fail for more experienced developers in sensitive production environments which should never block an event loop. 


Alexey Loubyansky

unread,
Jun 25, 2021, 4:59:19 AM6/25/21
to Paul Carter-Brown, Guillaume Smet, Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
IMO, the downside of adding @Blocking for users on-the-fly is it will take the category of users Guillaume mentioned further. I am not sure they deserve that :)

Paul Carter-Brown

unread,
Jun 25, 2021, 5:09:40 AM6/25/21
to Alexey Loubyansky, Guillaume Smet, Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
Yea, but you can't be all things to all people. If those same developers update a static int as a counter on an @ApplicationScoped bean then it's not like you are disallowing that...

Erin Schnabel

unread,
Jun 25, 2021, 8:34:32 AM6/25/21
to Paul Carter-Brown, Alexey Loubyansky, Guillaume Smet, Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
I was definitely under the impression we wanted to move to resteasy reactive as the default over time..
and that the ability to support for traditional blocking methods (because the imperative style still works) was a win.

I like Paul's idea a lot... it lets the user know what is going on (is forgiving when they don't know) and tells them how to fix it, but isn't a hard failure. That smooths the getting started path a lot.

Jason Greene

unread,
Jun 25, 2021, 12:31:23 PM6/25/21
to guillau...@gmail.com, Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list, Loïc MATHIEU

On Jun 25, 2021 at 2:44:27 AM, Guillaume Smet <guillau...@gmail.com> wrote
My guess is that in a reasonable future, we will want to make RESTEasy Reactive our default REST stack. IIRC, I advocated for blocking as default exactly for this reason back in the time: people used to reactive know what they are doing and can tweak things, people not aware of reactive can just use it without any hassle (and more than that, they are not even aware reactive exists and the constraints it comes with).

I originally was in the camp to make the default blocking. However, as I was saying in a chat with Stuart, I am a convert! The existing approach is awesome, and the usability impact is much smaller than I originally expected it would be (caveats to come).


First, though let’s talk about future.


I’d like to see us get off of the resteasy classic as the default ASAP (perhaps 2.1 or 2.2) and go all in on restesy-reactive. However, it’s been pointed out that the name causes confusion because resteasy-reactive isnt JUST reactive. It offers big gains for blocking as well. So to address that I propose we rename the extension to “quarkus-rest”. The library we use under the hood is still resteasy-reactive, but we talk about it as Quarkus Rest, and explain that Quarkus Rest is super fast REST for both scenarios, and it’s built on resteasy-reactive + vert.x. We would keep some kind of alias to avoid breaking people. We would then switch all our docs and guides and defaults to Quarkus REST, and formally deprecate the resteasy (“classic”) extension. 


Back to the main topic, I actually like the explicit declaration of Blocking. It makes the user actually think about, and understand, the I/O model they are using, and by doing so they avoid hidden performance problems. The small cost they pay is we force explicit declaration with a runtime validation error. Unlike the hidden surprises though, this feedback is effectively immediate and tells you what you need to fix. As also mentioned in the thread we could make this error message even more helpful.


Just to clarify on the hidden performance problems, what I mean is that a user does something truly stateless (simple redirection, static content of some form, computation, send a message. etc), or they try to use reactive to speed up some processing, it looks like it works, but then after deploying, they notice a bottleneck. Granted the fix is simple but the cost to find it is much higher than the immediate error scenario we are able to do with accidental usage of blocking. So defaulting to the easier-to-detect setting makes sense to me.  It also helps us when folks throw together a benchmark out of the box and make the wrong assumptions on how we compare to other modern reactive-defaulting stacks. Not that apple vs orange benchmarks should drive our decisions!


I like the idea of doing smart things if someone picks hibernate traditional, although my suggestion would be to generate @Blocking on the methods (or maybe the resource class), and not a separate @Application class, which is off hidden somewhere . Also ideally we would generate a full working hibernate basic scenario with update or drop-and-create table creation options specified. That way the user is good to go. 


In the future when we have Loom, we could probably look at just making the default be “coroutine”. 


Worst case scenario is everyone hates it and we need to look at changing it. 


Thoughts?

-Jason




Sanne Grinovero

unread,
Jun 25, 2021, 12:46:07 PM6/25/21
to Jason T. Greene, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list, Loïc MATHIEU


On Fri, 25 Jun 2021, 17:31 Jason Greene, <jason....@redhat.com> wrote:

On Jun 25, 2021 at 2:44:27 AM, Guillaume Smet <guillau...@gmail.com> wrote
My guess is that in a reasonable future, we will want to make RESTEasy Reactive our default REST stack. IIRC, I advocated for blocking as default exactly for this reason back in the time: people used to reactive know what they are doing and can tweak things, people not aware of reactive can just use it without any hassle (and more than that, they are not even aware reactive exists and the constraints it comes with).

I originally was in the camp to make the default blocking. However, as I was saying in a chat with Stuart, I am a convert! The existing approach is awesome, and the usability impact is much smaller than I originally expected it would be (caveats to come).


First, though let’s talk about future.


I’d like to see us get off of the resteasy classic as the default ASAP (perhaps 2.1 or 2.2) and go all in on restesy-reactive. However, it’s been pointed out that the name causes confusion because resteasy-reactive isnt JUST reactive. It offers big gains for blocking as well. So to address that I propose we rename the extension to “quarkus-rest”. The library we use under the hood is still resteasy-reactive, but we talk about it as Quarkus Rest, and explain that Quarkus Rest is super fast REST for both scenarios, and it’s built on resteasy-reactive + vert.x. We would keep some kind of alias to avoid breaking people. We would then switch all our docs and guides and defaults to Quarkus REST, and formally deprecate the resteasy (“classic”) extension. 


Back to the main topic, I actually like the explicit declaration of Blocking. It makes the user actually think about, and understand, the I/O model they are using, and by doing so they avoid hidden performance problems. The small cost they pay is we force explicit declaration with a runtime validation error. Unlike the hidden surprises though, this feedback is effectively immediate and tells you what you need to fix. As also mentioned in the thread we could make this error message even more helpful.


Just to clarify on the hidden performance problems, what I mean is that a user does something truly stateless (simple redirection, static content of some form, computation, send a message. etc), or they try to use reactive to speed up some processing, it looks like it works, but then after deploying, they notice a bottleneck. Granted the fix is simple but the cost to find it is much higher than the immediate error scenario we are able to do with accidental usage of blocking. So defaulting to the easier-to-detect setting makes sense to me.  It also helps us when folks throw together a benchmark out of the box and make the wrong assumptions on how we compare to other modern reactive-defaulting stacks. Not that apple vs orange benchmarks should drive our decisions!


I like the idea of doing smart things if someone picks hibernate traditional, although my suggestion would be to generate @Blocking on the methods (or maybe the resource class), and not a separate @Application class, which is off hidden somewhere .


Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Also I still don't see how any of this can play well with @Transactional.

Also ideally we would generate a full working hibernate basic scenario with update or drop-and-create table creation options specified. That way the user is good to go. 


In the future when we have Loom, we could probably look at just making the default be “coroutine”. 


Worst case scenario is everyone hates it and we need to look at changing it. 


Thoughts?

-Jason




--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Jason Greene

unread,
Jun 25, 2021, 12:56:17 PM6/25/21
to Sanne Grinovero, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list, Loïc MATHIEU
On Jun 25, 2021 at 11:45:54 AM, Sanne Grinovero <sa...@hibernate.org> wrote:


Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Right but AFAICT all of this is detected because we don’t do it based on API, we just wait till the first blocking operation gets ran, which will eventually happen since the db will block. Is there a scenario you are thinking of that I am missing? 



Also I still don't see how any of this can play well with @Transactional

Do you mean if someone is using say a Uni with @Transactional on the reactive rest method?

-Jason

Jason Greene

unread,
Jun 25, 2021, 1:11:52 PM6/25/21
to Jason Greene, Sanne Grinovero, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list, Loïc MATHIEU


On Jun 25, 2021, at 11:56 AM, Jason Greene <jason....@redhat.com> wrote:

Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Right but AFAICT all of this is detected because we don’t do it based on API, we just wait till the first blocking operation gets ran, which will eventually happen since the db will block. Is there a scenario you are thinking of that I am missing? 

More specially what I mean is that before the http  response returns and the method call completes one of two things will happen: a) the user made some blocking call which we immediately report as an error  (method throws exception) or b) nothing blocked and everything ran on the event loop fine. 

Since our user are using our live coding facilities they see this right away, and say we make the error better “Did you forget to say @Blocking on your resource class or method?”

Sanne Grinovero

unread,
Jun 25, 2021, 2:06:10 PM6/25/21
to Jason T. Greene, Sanne Grinovero, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list, Loïc MATHIEU
On Fri, 25 Jun 2021 at 18:11, Jason Greene <jason....@redhat.com> wrote:


On Jun 25, 2021, at 11:56 AM, Jason Greene <jason....@redhat.com> wrote:

Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Right but AFAICT all of this is detected because we don’t do it based on API, we just wait till the first blocking operation gets ran, which will eventually happen since the db will block. Is there a scenario you are thinking of that I am missing? 

More specially what I mean is that before the http  response returns and the method call completes one of two things will happen: a) the user made some blocking call which we immediately report as an error  (method throws exception) or b) nothing blocked and everything ran on the event loop fine. 

Ok I had misunderstood your proposal then; you said "my suggestion would be to generate @Blocking on the methods " which made me wonder how you can define the boundary of such methods, to decide where exactly the annotation needs to be added.

What you say now seems more like what Paul also suggested? +1 for Paul's idea.

Let's just remember that some calls might elude detection even though they are blocking if they happen to succeed quickly enough; this implies that "observing" only the first call won't be good enough - not if we need full certainty at least.

Thanks

Jason Greene

unread,
Jun 25, 2021, 3:27:22 PM6/25/21
to Sanne Grinovero, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list, Loïc MATHIEU
On Jun 25, 2021 at 1:05:56 PM, Sanne Grinovero <sa...@hibernate.org> wrote:

On Fri, 25 Jun 2021 at 18:11, Jason Greene <jason....@redhat.com> wrote:


On Jun 25, 2021, at 11:56 AM, Jason Greene <jason....@redhat.com> wrote:

Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Right but AFAICT all of this is detected because we don’t do it based on API, we just wait till the first blocking operation gets ran, which will eventually happen since the db will block. Is there a scenario you are thinking of that I am missing? 

More specially what I mean is that before the http  response returns and the method call completes one of two things will happen: a) the user made some blocking call which we immediately report as an error  (method throws exception) or b) nothing blocked and everything ran on the event loop fine. 

Ok I had misunderstood your proposal then; you said "my suggestion would be to generate @Blocking on the methods " which made me wonder how you can define the boundary of such methods, to decide where exactly the annotation needs to be added.

Ah sorry for the confusion.


What you say now seems more like what Paul also suggested? +1 for Paul's idea.

I was talking about a variant of Stuart’s idea #3, our code generator for creating projects , that if you pick a project with JPA, we just add @Blocking to the generated example methods on the resource using hibernate 

Paul I think was suggesting automatic switchover. I like the out of the box thinking. Its an interesting idea. I imagine with restarting it would get very noisy, which would encourage developers to fix this stuff right away anyway, but maybe if they are in the middle of something they just want to see the code output and it would be helpful.

Let's just remember that some calls might elude detection even though they are blocking if they happen to succeed quickly enough; this implies that "observing" only the first call won't be good enough - not if we need full certainty at least.

That’s true.  My thinking is since users are developing interactively they should hit it pretty quickly even if you get something that is narrow enough to escape. 

Georgios Andrianakis

unread,
Jun 25, 2021, 3:36:03 PM6/25/21
to Jason Greene, Sanne Grinovero, Guillaume Smet, Alexey Loubyansky, Stuart Douglas, Quarkus Development mailing list, Loïc MATHIEU
Here is something even more radical (that we don't have the infrastructure in place for):

We automatically make the fix in the code itself, i.e. give dev-mode the ability to fix your code, in this case to add @Blocking when the exception occurs

Kristijan Rusu

unread,
Jun 25, 2021, 4:53:22 PM6/25/21
to Quarkus Development mailing list
Hello everyone, sorry for sharing my two peas, which may be off-topic. 
As I understood from the conversation, Quarkus wants to deprecate blocking REST, which means that also blocking JPA will be deprecated. If this is the case, the users should be notified really early about the changes, because some of use have long lived project commitments (talking about the project will be active at least 4-5 years from now) and we do like Quarkus a lot, but do not want to use Reactive everywhere. We do know is faster, but not as easy for onboarding new people and for usage. We use Reactive only where we need performance. Meaning the project contains multiple microservices, which few of them use the RESTEasy Reactive, but most of them use the RESTEasy classic, which is why we chose Quarkus in the first place. If this kind of change is coming, my opinion is to share the decision as early as possible, as surely we would drop Quarkus if we're pushed to go Reactive. We're pretty happy with blocking code as not all of our microservices are mission critical, as most apps out there. 
Thank you. 

Kristijan Rusu

unread,
Jun 25, 2021, 4:58:30 PM6/25/21
to Quarkus Development mailing list
Forgot to mention, I think the current way, with a single change where Reactive is maybe default for JPA and REST, will be okay, but dropping blocking altogether may be not a good option. If you add blocking / reactive dependency, you should know what you're doing, or learn the hard way.

Jason Greene

unread,
Jun 25, 2021, 5:37:12 PM6/25/21
to kristij...@gmail.com, Quarkus Development mailing list
Hi Kristijan,

I can 100% emphatically confirm that Quarkus WILL NOT DROP Imperative/blocking, probably ever. It’s part of our core value proposition.

We are just talking about two implementation stacks we have (RESTEasy classic is blocking only, RESTEasy Reactive is both) of the same APIs. Even in that case we there would be a phaseout on the older stack, and community discussion on any final decision to remove. 

Sorry for the confusion, and Thank you for speaking up!

-Jason

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Erin Schnabel

unread,
Jun 26, 2021, 9:25:39 AM6/26/21
to jason....@redhat.com, Quarkus Development mailing list, kristij...@gmail.com
Quandry for me: this introduces friction for anyone bringing code over from some other existing app, and somewhat violates the principle of least surprise: they used the code they were accustomed to, and behavior changed (their code failed with an exception, and they have to add an annotation to make it behave as they expect). 

I remember that the discussions about this last time went on for awhile.. but for signatures that match old / existing / “classic” signatures, I feel like the safest/least-surprising choice should be blocking. Using an async or reactive return type (Uni/Multi/*Future/something) would automatically trigger async behavior.. 

Now this is obviously counter to what we want (which is more folks getting the benefit of non-blocking/async behavior). 

So if we indeed want to do that (and make resteasy reactive the default stack), we need to make the experience less “surprising” in the negative sense… Their code should just work, to the greatest extent we can manage, which could mean compensating when they do something that we know is a blocking activity in an async context. We know enough to throw the exception… if we can do something nicer, we should. 

--
Thanks,
Erin
 
------
Erin Schnabel <ebul...@redhat.com>
@ebullientworks

Georgios Andrianakis

unread,
Jun 26, 2021, 9:33:02 AM6/26/21
to Erin Schnabel, Jason Greene, Quarkus Development mailing list, kristij...@gmail.com


On Sat, Jun 26, 2021, 16:25 Erin Schnabel <ebul...@redhat.com> wrote:
Quandry for me: this introduces friction for anyone bringing code over from some other existing app, and somewhat violates the principle of least surprise: they used the code they were accustomed to, and behavior changed (their code failed with an exception, and they have to add an annotation to make it behave as they expect). 

I remember that the discussions about this last time went on for awhile.. but for signatures that match old / existing / “classic” signatures, I feel like the safest/least-surprising choice should be blocking. Using an async or reactive return type (Uni/Multi/*Future/something) would automatically trigger async behavior.. 

Micronaut 1.x did this and they reversed course with 2.x as they found it caused to many problems in practice (I can find the GH issue with the long discussion if you like).

Now this is obviously counter to what we want (which is more folks getting the benefit of non-blocking/async behavior). 

So if we indeed want to do that (and make resteasy reactive the default stack), we need to make the experience less “surprising” in the negative sense… Their code should just work, to the greatest extent we can manage, which could mean compensating when they do something that we know is a blocking activity in an async context. We know enough to throw the exception… if we can do something nicer, we should. 

I can see the merit in this, but it still feels weird to me. If feels too surprising

Paul Carter-Brown

unread,
Jun 26, 2021, 10:49:59 AM6/26/21
to Georgios Andrianakis, Erin Schnabel, Jason Greene, Quarkus Development mailing list, kristij...@gmail.com
My performance tests show that if we consider RR on eventloop to be the ultimate, and compare plain resteasy and RR with @Blocking they stack up as follows in terms of end to end latency of a simple hello world rest service using wrk as the load test tool on localhost:

RR on eventloop: 23 microseconds
RR with @Blocking: 31 microseconds
Normal resteasy extension: 48 microseconds
Normal resteasy and undertow extensions: 52 microseconds

So the question I ask myself is this - would a developer who does not know or care about eventloops and blocking and reactive and worker pools, care about 8 microseconds? Someone who does care about those 8 microseconds would know to use @NonBlocking on their endpoint which is so latency sensitive. Light only travels 2.4km in 8 microseconds - people who care about that are not running codestarts with default settings on their production environments... I'm all for "make it work out the box" and then if you want to run a TechEmpower competition or build a real time trading platform then you know you have some tweeks you can do but with the caution and knowledge of an experienced developer. 

O, and btw, Micronaut 3 on eventloop sits at 38 microseconds.... 

Georgios Andrianakis

unread,
Jun 26, 2021, 10:55:33 AM6/26/21
to Paul Carter-Brown, Erin Schnabel, Jason Greene, Quarkus Development mailing list, kristij...@gmail.com


On Sat, Jun 26, 2021, 17:50 Paul Carter-Brown <pa...@ukheshe.com> wrote:
My performance tests show that if we consider RR on eventloop to be the ultimate, and compare plain resteasy and RR with @Blocking they stack up as follows in terms of end to end latency of a simple hello world rest service using wrk as the load test tool on localhost:

RR on eventloop: 23 microseconds
RR with @Blocking: 31 microseconds
Normal resteasy extension: 48 microseconds
Normal resteasy and undertow extensions: 52 microseconds

So the question I ask myself is this - would a developer who does not know or care about eventloops and blocking and reactive and worker pools, care about 8 microseconds? Someone who does care about those 8 microseconds would know to use @NonBlocking on their endpoint which is so latency sensitive. Light only travels 2.4km in 8 microseconds - people who care about that are not running codestarts with default settings on their production environments... I'm all for "make it work out the box" and then if you want to run a TechEmpower competition or build a real time trading platform then you know you have some tweeks you can do but with the caution and knowledge of an experienced developer. 

This is a very good argument.

But we also need to consider cases where developers or architects compare the performance of various frameworks in hello world type settings.
In such cases, having the highest performing (in terms of throughout and latency as the number of requests increase) option with no changes necessary could prove advantageous.

Paul Carter-Brown

unread,
Jun 26, 2021, 11:18:59 AM6/26/21
to Georgios Andrianakis, Erin Schnabel, Jason Greene, Quarkus Development mailing list, kristij...@gmail.com
Agreed, and for those the adaptive mode would work perfectly. You dont want someone evaluating Quarkusand be really impressed and then add a SQL query and suddenly it starts throwing errors. They will change to @Blocking and feel like all their previous tests were now meaningless.

Michał Szynkiewicz

unread,
Jun 26, 2021, 4:02:21 PM6/26/21
to pa...@ukheshe.com, Georgios Andrianakis, Erin Schnabel, Jason Greene, Quarkus Development mailing list, kristij...@gmail.com
I like how we do it now. Maybe the error message could be improved further but we fail fast. It will fail in a test, or in manual testing in dev mode if someone starts doing blocking things. 
I like it more than the adaptive approach. It is straightforward, users know what is happening, no hidden surprises.

But I agree with Stuart that blocking by default is much easier for new users. 
And it's better to change it sooner rather than later.
I like simple, for me if we are to switch to blocking by default, a property (in application.properties) to override it would be best.

And we already have `@NonBlocking` that could be used to mark endpoints as non-blocking if the default is set to blocking.



Jason Greene

unread,
Jun 26, 2021, 9:21:50 PM6/26/21
to Paul Carter-Brown, Erin Schnabel, Quarkus Development mailing list, kristij...@gmail.com, Georgios Andrianakis
It can be quite a bit worse than that.  The thing with blocking is it runs great up until you exceed the pool size, and then your throughput degrades rapidly. The value of reactive is that with just a few threads you can scale from zero to massive load volume. So the difference is really about scalability. 

If you run a reactive task through a thread pool that scales better (since it typically returns right away), but you start to hit contention on that pool. The bigger issue though is you break a critical locality optimization and assumption in modern reactive stacks like vert.x. Everything is built around the assumption that request state is pinned to the event loop thread (which are partitioned across many many clients).  In addition to hurting reactive performance you can also end up running into subtle issues. 

Not specifying Blocking in a reactive execution pattern  gets you a fail fast error, Not specifying NonBlocking in a reactive execution pattern looks like everything is normal. It’s not till you notice performance issues or weird bugs that you realize ah ha I forgot NonBlocking. 

That said we might be able to warn on some conditions. We could also detect reactive signatures that involve Mutiny BUT, like Erin suggests, but there are also reactive code patterns that return pojos or buffers, so lots of cases can’t be converted automatically.

The nice thing about the code generation approach (solution 3) is that you probably don’t even hit the the fail-fast error, since the generated code has everything properly specified. 

Jason Greene

unread,
Jun 26, 2021, 9:28:22 PM6/26/21
to Michał Szynkiewicz, Georgios Andrianakis, Erin Schnabel, Quarkus Development mailing list, kristij...@gmail.com, pa...@ukheshe.com
On Jun 26, 2021 at 3:02:07 PM, Michał Szynkiewicz <michal.l.s...@gmail.com> wrote:
I like how we do it now. Maybe the error message could be improved further but we fail fast. It will fail in a test, or in manual testing in dev mode if someone starts doing blocking things. 
I like it more than the adaptive approach. It is straightforward, users know what is happening, no hidden surprises.

But I agree with Stuart that blocking by default is much easier for new users. 
And it's better to change it sooner rather than later.

Sadly we are already pretty late on this, since we already tagged 2.0. It’s good to make sure before we start making plans to switch the default. 

Some other solutions to throw out there BTW (I didn’t think that deeply about them!):

  1. We could treat non-specification (either inherited or explicit) as a fail fast. In that way we would be preference-free at the framework level (with a lean towards blocking with our code generator).
  2. We could stick with two extensions which change the default (and maybe other things). They would both be backed by the same impl, but basically the extension set would drive the behavior.

I like simple, for me if we are to switch to blocking by default, a property (in application.properties) to override it would be best.

It’s worth considering but it feels like a mismatch. Properties are supposed to be independent of code, and this type of setting has only one legal value for a given set of code (where the code defines that legal setting).

-Jason

Paul Carter-Brown

unread,
Jun 27, 2021, 4:01:10 AM6/27/21
to Jason Greene, Erin Schnabel, Quarkus Development mailing list, kristij...@gmail.com, Georgios Andrianakis
Agreed. I'm just referring to the impact of non blocking code having @Blocking. If code is non blocking then it cant exhaust the worker pool any worse than it can exhaust the event loop pool.


The merits of writing reactive code for scalability are clear. For me the debate is whether to automatically or by default use the worker pool when code is blocking or fail or warn.

clement escoffier

unread,
Jun 27, 2021, 5:41:02 AM6/27/21
to pa...@ukheshe.com, Erin Schnabel, Quarkus Development mailing list, kristij...@gmail.com, Georgios Andrianakis, Jason Greene
Hello,

While the exception can be improved, I would keep the current behaviour. 
In addition to the production issues highlighted by Jason (which leads to create multiple instances of the service, and so goes against the deployment density gain of Quarkus), we would need to change lots of other extensions that use the same model (messaging, gRPC…), or we will introduce confusion when mixing extensions. 

We could add an attribute while decide the default, however, as said, it would need to be handled by multiple extensions (and because these extensions are no-jax-rs, it can’t be an Application class). 

One way to avoid the issue would be to introduce application archetypes when creating a new application (don’t be mistaken, this is not about Maven archetype). We have a way to decide what class of application you are using, we could 1. Curate the list of extension to expose, 2. Influence the attribute (if we have one).  These would be seen in code.quarkus.io as well as in the CLI (where you already decide CLI vs. App)

For example:

  • CLI - blocking rest client (reactive could make sense, but it’s a fairly advanced use case)
  • Classic / Blocking CRUD  - blocking + regular hibernate + JDBC
  • Reactive CRUD - non-blocking, HR
  • ...

Clement





William Burke

unread,
Jun 27, 2021, 6:24:01 AM6/27/21
to Erin Schnabel, Jason Greene, Quarkus Development mailing list, kristij...@gmail.com
On Sat, Jun 26, 2021 at 9:25 AM Erin Schnabel <ebul...@redhat.com> wrote:

Now this is obviously counter to what we want (which is more folks getting the benefit of non-blocking/async behavior). 


As a tangent, this is certainly not something I want.  My long standing beef is that non-blocking (in general) is too hard to understand, read, follow, maintain, debug, code, and looks ugly as hell.  Non-blocking is something I hope Loom will make an edge case of.  Cheers and my 2cents FWIW.

René Grob

unread,
Jun 27, 2021, 11:36:57 AM6/27/21
to Quarkus Development mailing list
Hi everyone,

Why not letting the user decide? You could enforce that every method needs to be configured to either be blocking or non-blocking. Of course it's too cumbersome to do that on each and every method but you could offer an optional global default in application.properties (but if it is not set, there's no default on that level), and another on the Application class if present and then on class level, and on method level. Like that the user needs to take a choice and has full flexibility. Also there's no auto-magic involved which can be as annoying as it can be helpful. Don't set a default! Let the user set it or let the user decide on the level he/she's comfortable with.

WDYT?

René

clement escoffier

unread,
Jun 27, 2021, 1:48:13 PM6/27/21
to rene....@gmail.com, Quarkus Development mailing list
Yes, letting the user decide would be best. Isn’t it that different from what we have today?

As a reminder, It’s not limited to RestEasy, but several other extensions are involved (it also means that the Application class cannot be used). If we decide to go in this direction, we need to update several extensions and upstream projects. That is going to take some time.

If we do the change only for RestEasy, we need to rework some integrations as the other extensions will offer a different execution model, and the mix (calling one from the other) would not be without consequences.

Clement

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Stuart Douglas

unread,
Jun 27, 2021, 10:15:35 PM6/27/21
to Paul Carter-Brown, Alexey Loubyansky, Guillaume Smet, Loïc MATHIEU, Georgios Andrianakis, Quarkus Development mailing list
On Fri, 25 Jun 2021 at 18:49, Paul Carter-Brown <pa...@ukheshe.com> wrote:
I know it would be complex to implement but the best option IMHO is to instead of throwing an exception when blocking on an event loop thread, rather flag the rest resource method as blocking, log a warning and from that point on in the lifetime of the process, pretend the developer added @Blocking annotation on the method.

We had actually discussed this during development, but we did not pursue it because it felt a bit too 'magical'.

I think the initial failure is a bit of a problem though. You could re-submit the same request on a blocking worker however that may cause problems if the request is not idempotent.

Stuart
 

This would mean that:
a) Any example or combination of extensions would always "work" OOTB
b) If you don't block or do anything special, you get the best performance possible
c) If you end up blocking an event loop, it won't happen again on that method

Perhaps there could be a config setting for resteasy reactive for eventloop blocking policy - something like:
- fail: do what it does currently
- warn: log a warning and continue and don't adapt to blocking for subsequent calls
- auto: log a warning and adapt to blocking for subsequent calls

Then I would make it auto by default and encourage using fail for more experienced developers in sensitive production environments which should never block an event loop. 


On Fri, Jun 25, 2021 at 9:59 AM Alexey Loubyansky <alexey.l...@redhat.com> wrote:
On Fri, Jun 25, 2021 at 9:45 AM Guillaume Smet <guillau...@gmail.com> wrote:
The problem is not with users who know what they are doing. The problem is that most users have absolutely no idea what reactive means (and don't care about learning about it, they just want to keep doing what they are doing). If going reactive didn't have any trade-off, we could just keep our position and say "just learn this new thing" but that's not the case.

If users select a reactive extension and don't know what reactive means, and don't care about learning what that means, I'd say it's their problem. IMO, the exception we are throwing in case of JPA/JDBC is of great help (even to those who know what they are doing but simply missed the @Blocking in the code).

My guess is that in a reasonable future, we will want to make RESTEasy Reactive our default REST stack. IIRC, I advocated for blocking as default exactly for this reason back in the time: people used to reactive know what they are doing and can tweak things, people not aware of reactive can just use it without any hassle (and more than that, they are not even aware reactive exists and the constraints it comes with).

If we do change to the resteasy-reactive as the default JAX-RS extension that'll change the situation. Currently, it's an explicit user's choice.

I suppose taking into account the extensions could be an acceptable trade-off, even if not perfect (given some extensions expose both paradigms as mentioned by Loïc). In that case, I would go the blocking route by default and go with the comment Martin asked for.

On Fri, Jun 25, 2021 at 9:31 AM Loïc MATHIEU <loik...@gmail.com> wrote:
It's more complex than that, what will you do when a user includes the MongoDB extension which supports both blocking and reactive ?
What if a user includes Hibernate ORM and some other reactive extension, and always dispatches to a worker thread (via a subscribeOn() method) because he knows what he's doing ?


Le ven. 25 juin 2021 à 09:04, Alexey Loubyansky <alexey.l...@redhat.com> a écrit :
A possible argument against changing the default could be that the resteasy-reactive isn't the default JAX-RS extension. To have it in the app it (or one of the reactive extensions that depend on it) has to be added to the project by the user. "Reactive" is in its name, which should raise awareness.

On Fri, Jun 25, 2021 at 8:16 AM Georgios Andrianakis <gand...@redhat.com> wrote:
Thanks for bringing it up Stuart,

I personally think 3 is the best solution, but 2 would also be nice.
Avoiding 1 should be our goal I think because not only would it be surprising for existing users, but it would also make it more difficult that solution 2 to get the full power of the Reactive behavior

On Fri, Jun 25, 2021, 09:11 Stuart Douglas <sdou...@redhat.com> wrote:
Hi Everyone,

I would like to revisit the topic of the default blocking vs non-blocking for RESTEasy Reactive. As we start to push users more towards RESTEasy Reactive I think the non-blocking by default behaviour is problematic for new users. For example if I generate an application with RESTEasy Reactive an Panache, and then attempt to use a panache entity in the endpoint I end up with:

"java.lang.IllegalStateException: You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking EntityManager operations make sure you are doing it from a worker thread."

Now we could improve this error message to try and detect what has happened and tell the user to use @Blocking, however it's still not a great user experience where the most basic of CRUD endpoints fails out of the box.

I am not really sure what the best solution is here, but I think we have a few options:

1) Change the default to @Blocking, but in a smart way so we can still dispatch to @Async endpoints without switching to a worker thread

I think it is more likely that a reactive programmer will understand the difference between worker and IO threads, so it makes more sense to make the reactive programmer have to change the default, while someone just getting started may not have a clue why they need @Blocking or what the difference is.

2) Always include an Application class with @Blocking in the generated codestart
3) Include an Application class with @Blocking in the generated codestart if any blocking extensions are selected

E.g. if you include Hibernate ORM you will end up with an app configured for blocking by default, if you only include reactive extensions you won't.

I don't super like either of these options, but in terms of making the getting started experience easy maybe we should change the default? I think leaving things as they are will potentially be a barrier to new users if we start to push RESTEasy Reactive more.

What do people think?

Stuart

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Stuart Douglas

unread,
Jun 27, 2021, 10:20:27 PM6/27/21
to Sanne Grinovero, Jason T. Greene, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Quarkus Development mailing list, Loïc MATHIEU
On Sat, 26 Jun 2021 at 02:46, Sanne Grinovero <sa...@hibernate.org> wrote:


On Fri, 25 Jun 2021, 17:31 Jason Greene, <jason....@redhat.com> wrote:

On Jun 25, 2021 at 2:44:27 AM, Guillaume Smet <guillau...@gmail.com> wrote
My guess is that in a reasonable future, we will want to make RESTEasy Reactive our default REST stack. IIRC, I advocated for blocking as default exactly for this reason back in the time: people used to reactive know what they are doing and can tweak things, people not aware of reactive can just use it without any hassle (and more than that, they are not even aware reactive exists and the constraints it comes with).

I originally was in the camp to make the default blocking. However, as I was saying in a chat with Stuart, I am a convert! The existing approach is awesome, and the usability impact is much smaller than I originally expected it would be (caveats to come).


First, though let’s talk about future.


I’d like to see us get off of the resteasy classic as the default ASAP (perhaps 2.1 or 2.2) and go all in on restesy-reactive. However, it’s been pointed out that the name causes confusion because resteasy-reactive isnt JUST reactive. It offers big gains for blocking as well. So to address that I propose we rename the extension to “quarkus-rest”. The library we use under the hood is still resteasy-reactive, but we talk about it as Quarkus Rest, and explain that Quarkus Rest is super fast REST for both scenarios, and it’s built on resteasy-reactive + vert.x. We would keep some kind of alias to avoid breaking people. We would then switch all our docs and guides and defaults to Quarkus REST, and formally deprecate the resteasy (“classic”) extension. 


Back to the main topic, I actually like the explicit declaration of Blocking. It makes the user actually think about, and understand, the I/O model they are using, and by doing so they avoid hidden performance problems. The small cost they pay is we force explicit declaration with a runtime validation error. Unlike the hidden surprises though, this feedback is effectively immediate and tells you what you need to fix. As also mentioned in the thread we could make this error message even more helpful.


Just to clarify on the hidden performance problems, what I mean is that a user does something truly stateless (simple redirection, static content of some form, computation, send a message. etc), or they try to use reactive to speed up some processing, it looks like it works, but then after deploying, they notice a bottleneck. Granted the fix is simple but the cost to find it is much higher than the immediate error scenario we are able to do with accidental usage of blocking. So defaulting to the easier-to-detect setting makes sense to me.  It also helps us when folks throw together a benchmark out of the box and make the wrong assumptions on how we compare to other modern reactive-defaulting stacks. Not that apple vs orange benchmarks should drive our decisions!


I like the idea of doing smart things if someone picks hibernate traditional, although my suggestion would be to generate @Blocking on the methods (or maybe the resource class), and not a separate @Application class, which is off hidden somewhere .


Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Also I still don't see how any of this can play well with @Transactional.

We could potentially treat @Transactional the same as @Blocking, so automatically move to blocking for transactional endpoints, it may be a bit confusing for users though.

Stuart

Stuart Douglas

unread,
Jun 27, 2021, 10:22:51 PM6/27/21
to Sanne Grinovero, Jason T. Greene, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Quarkus Development mailing list, Loïc MATHIEU
On Sat, 26 Jun 2021 at 04:06, Sanne Grinovero <sa...@hibernate.org> wrote:

On Fri, 25 Jun 2021 at 18:11, Jason Greene <jason....@redhat.com> wrote:


On Jun 25, 2021, at 11:56 AM, Jason Greene <jason....@redhat.com> wrote:

Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Right but AFAICT all of this is detected because we don’t do it based on API, we just wait till the first blocking operation gets ran, which will eventually happen since the db will block. Is there a scenario you are thinking of that I am missing? 

More specially what I mean is that before the http  response returns and the method call completes one of two things will happen: a) the user made some blocking call which we immediately report as an error  (method throws exception) or b) nothing blocked and everything ran on the event loop fine. 

Ok I had misunderstood your proposal then; you said "my suggestion would be to generate @Blocking on the methods " which made me wonder how you can define the boundary of such methods, to decide where exactly the annotation needs to be added.

What you say now seems more like what Paul also suggested? +1 for Paul's idea.

Let's just remember that some calls might elude detection even though they are blocking if they happen to succeed quickly enough; this implies that "observing" only the first call won't be good enough - not if we need full certainty at least.

In most cases we have detection that looks at the current thread, rather than relying on anything time based. We actually need this to implement blocking on top of NIO to avoid deadlocks, if you block the IO thread in one of our InputStream implementations you would deadlock as the IO Thread is the thread that delivers the data and unblocks the waiting thread.

Stuart

Stephane Epardaud

unread,
Jun 28, 2021, 4:01:32 AM6/28/21
to Paul Carter-Brown, Georgios Andrianakis, Erin Schnabel, Jason Greene, Quarkus Development mailing list, kristij...@gmail.com
On Sat, 26 Jun 2021 at 16:49, Paul Carter-Brown <pa...@ukheshe.com> wrote:
So the question I ask myself is this - would a developer who does not know or care about eventloops and blocking and reactive and worker pools, care about 8 microseconds? 

Well, 8ms doesn't seem like much, but it's still a 34% drop in perf: all alone it's peanuts, but under load it's a huge number.

Sanne Grinovero

unread,
Jun 28, 2021, 5:21:41 AM6/28/21
to Stuart Douglas, Sanne Grinovero, Jason T. Greene, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Quarkus Development mailing list, Loïc MATHIEU
Right, for example if someone invokes a method on Hibernate Reactive on a non-vertx thread we move the operation to a vertx context - by checking the thread type. I guess that's a good start - are you suggesting we should have such checks more pervasively across all frameworks?

For example I suppose we could patch Hibernate ORM (blocking) to do the opposite: check that it's not running on a vertx thread, and if it does to trigger some [warning|exception].

But I doubt it's going to be practical to gap all holes in all libraries, especially if we have to include registered callbacks and pluggable extension points?  I guess we could cover a great deal of them but I wouldn't assume we can create a water tight net to catch all situations, and it would be a bit weird to have users come to rely on such a mechanism if in practice we can't catch them all. So back to my point: sure we can do this but let's set the right expectations, as in we can't detect all cases. It will certainly require a combination of looking at thread types and checking the vertx-is-stuck flags.

Besides, there's many practical tricks people do use on ORM blocking which will make some of its usage non-blocking; it would be very annoying for the "classic" users to start spewing out exceptions or warnings for perfectly acceptable cases. For example on many projects I've worked on, there would be a fair amount of entities identified which are extremely hot in the read path of the application, and people would use various techniques to ensure 100% cache hit ratios.
To do this, you'd most likely have a bootstrap method filling in such caches, or just warmup the application by hitting the same endpoints as you'd normally receive traffic on: during this warmup phase it would be "acceptable" to block exceptionally, as it's never going to block when under production load even though it might block once on first call.

So if you want to go this path, please ensure people can turn it off if they want to.

Thanks,
Sanne



 

Stuart
 

Thanks


Since our user are using our live coding facilities they see this right away, and say we make the error better “Did you forget to say @Blocking on your resource class or method?”

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Stuart Douglas

unread,
Jun 28, 2021, 5:34:25 AM6/28/21
to Sanne Grinovero, Jason T. Greene, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Quarkus Development mailing list, Loïc MATHIEU
On Mon, 28 Jun 2021 at 19:21, Sanne Grinovero <sa...@hibernate.org> wrote:


On Mon, 28 Jun 2021 at 03:22, Stuart Douglas <sdou...@redhat.com> wrote:


On Sat, 26 Jun 2021 at 04:06, Sanne Grinovero <sa...@hibernate.org> wrote:

On Fri, 25 Jun 2021 at 18:11, Jason Greene <jason....@redhat.com> wrote:


On Jun 25, 2021, at 11:56 AM, Jason Greene <jason....@redhat.com> wrote:

Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Right but AFAICT all of this is detected because we don’t do it based on API, we just wait till the first blocking operation gets ran, which will eventually happen since the db will block. Is there a scenario you are thinking of that I am missing? 

More specially what I mean is that before the http  response returns and the method call completes one of two things will happen: a) the user made some blocking call which we immediately report as an error  (method throws exception) or b) nothing blocked and everything ran on the event loop fine. 

Ok I had misunderstood your proposal then; you said "my suggestion would be to generate @Blocking on the methods " which made me wonder how you can define the boundary of such methods, to decide where exactly the annotation needs to be added.

What you say now seems more like what Paul also suggested? +1 for Paul's idea.

Let's just remember that some calls might elude detection even though they are blocking if they happen to succeed quickly enough; this implies that "observing" only the first call won't be good enough - not if we need full certainty at least.

In most cases we have detection that looks at the current thread, rather than relying on anything time based. We actually need this to implement blocking on top of NIO to avoid deadlocks, if you block the IO thread in one of our InputStream implementations you would deadlock as the IO Thread is the thread that delivers the data and unblocks the waiting thread.

Right, for example if someone invokes a method on Hibernate Reactive on a non-vertx thread we move the operation to a vertx context - by checking the thread type. I guess that's a good start - are you suggesting we should have such checks more pervasively across all frameworks?

For example I suppose we could patch Hibernate ORM (blocking) to do the opposite: check that it's not running on a vertx thread, and if it does to trigger some [warning|exception].

But I doubt it's going to be practical to gap all holes in all libraries, especially if we have to include registered callbacks and pluggable extension points?  I guess we could cover a great deal of them but I wouldn't assume we can create a water tight net to catch all situations, and it would be a bit weird to have users come to rely on such a mechanism if in practice we can't catch them all. So back to my point: sure we can do this but let's set the right expectations, as in we can't detect all cases. It will certainly require a combination of looking at thread types and checking the vertx-is-stuck flags.

Besides, there's many practical tricks people do use on ORM blocking which will make some of its usage non-blocking; it would be very annoying for the "classic" users to start spewing out exceptions or warnings for perfectly acceptable cases. For example on many projects I've worked on, there would be a fair amount of entities identified which are extremely hot in the read path of the application, and people would use various techniques to ensure 100% cache hit ratios.
To do this, you'd most likely have a bootstrap method filling in such caches, or just warmup the application by hitting the same endpoints as you'd normally receive traffic on: during this warmup phase it would be "acceptable" to block exceptionally, as it's never going to block when under production load even though it might block once on first call.

So if you want to go this path, please ensure people can turn it off if they want to.


Guillaume Smet

unread,
Jun 28, 2021, 5:46:36 AM6/28/21
to Alexey Loubyansky, Loïc MATHIEU, Georgios Andrianakis, Stuart Douglas, Quarkus Development mailing list
On Fri, Jun 25, 2021 at 9:59 AM Alexey Loubyansky <alexey.l...@redhat.com> wrote:
On Fri, Jun 25, 2021 at 9:45 AM Guillaume Smet <guillau...@gmail.com> wrote:
The problem is not with users who know what they are doing. The problem is that most users have absolutely no idea what reactive means (and don't care about learning about it, they just want to keep doing what they are doing). If going reactive didn't have any trade-off, we could just keep our position and say "just learn this new thing" but that's not the case.

If users select a reactive extension and don't know what reactive means, and don't care about learning what that means, I'd say it's their problem. IMO, the exception we are throwing in case of JPA/JDBC is of great help (even to those who know what they are doing but simply missed the @Blocking in the code).

I think that's the point of this discussion: RESTEasy Reactive is not really a reactive extension despite its name. It supports both blocking and non-blocking. And if we make it the default and sorta retire RESTEasy Classic, it will be used for both purposes so also by people who know absolutely nothing about reactive (and there are a lot of them).

Georgios Andrianakis

unread,
Jun 28, 2021, 5:48:02 AM6/28/21
to Guillaume Smet, Alexey Loubyansky, Loïc MATHIEU, Stuart Douglas, Quarkus Development mailing list
Which is why I agree that the extension should be renamed quarkus-rest and Option 3 of what Stuart proposes be adopted

Sanne Grinovero

unread,
Jun 28, 2021, 6:29:53 AM6/28/21
to Stuart Douglas, Sanne Grinovero, Jason T. Greene, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Quarkus Development mailing list, Loïc MATHIEU
On Mon, 28 Jun 2021 at 10:34, Stuart Douglas <sdou...@redhat.com> wrote:


On Mon, 28 Jun 2021 at 19:21, Sanne Grinovero <sa...@hibernate.org> wrote:


On Mon, 28 Jun 2021 at 03:22, Stuart Douglas <sdou...@redhat.com> wrote:


On Sat, 26 Jun 2021 at 04:06, Sanne Grinovero <sa...@hibernate.org> wrote:

On Fri, 25 Jun 2021 at 18:11, Jason Greene <jason....@redhat.com> wrote:


On Jun 25, 2021, at 11:56 AM, Jason Greene <jason....@redhat.com> wrote:

Remember lazy loading of entities and their attributes is a key aspect of programming with JPA.

If you have JPA in the application, we won't be able to easily identify which calls to which APIs are blocking as potentially any user's code could block.

Right but AFAICT all of this is detected because we don’t do it based on API, we just wait till the first blocking operation gets ran, which will eventually happen since the db will block. Is there a scenario you are thinking of that I am missing? 

More specially what I mean is that before the http  response returns and the method call completes one of two things will happen: a) the user made some blocking call which we immediately report as an error  (method throws exception) or b) nothing blocked and everything ran on the event loop fine. 

Ok I had misunderstood your proposal then; you said "my suggestion would be to generate @Blocking on the methods " which made me wonder how you can define the boundary of such methods, to decide where exactly the annotation needs to be added.

What you say now seems more like what Paul also suggested? +1 for Paul's idea.

Let's just remember that some calls might elude detection even though they are blocking if they happen to succeed quickly enough; this implies that "observing" only the first call won't be good enough - not if we need full certainty at least.

In most cases we have detection that looks at the current thread, rather than relying on anything time based. We actually need this to implement blocking on top of NIO to avoid deadlocks, if you block the IO thread in one of our InputStream implementations you would deadlock as the IO Thread is the thread that delivers the data and unblocks the waiting thread.

Right, for example if someone invokes a method on Hibernate Reactive on a non-vertx thread we move the operation to a vertx context - by checking the thread type. I guess that's a good start - are you suggesting we should have such checks more pervasively across all frameworks?

For example I suppose we could patch Hibernate ORM (blocking) to do the opposite: check that it's not running on a vertx thread, and if it does to trigger some [warning|exception].

But I doubt it's going to be practical to gap all holes in all libraries, especially if we have to include registered callbacks and pluggable extension points?  I guess we could cover a great deal of them but I wouldn't assume we can create a water tight net to catch all situations, and it would be a bit weird to have users come to rely on such a mechanism if in practice we can't catch them all. So back to my point: sure we can do this but let's set the right expectations, as in we can't detect all cases. It will certainly require a combination of looking at thread types and checking the vertx-is-stuck flags.

Besides, there's many practical tricks people do use on ORM blocking which will make some of its usage non-blocking; it would be very annoying for the "classic" users to start spewing out exceptions or warnings for perfectly acceptable cases. For example on many projects I've worked on, there would be a fair amount of entities identified which are extremely hot in the read path of the application, and people would use various techniques to ensure 100% cache hit ratios.
To do this, you'd most likely have a bootstrap method filling in such caches, or just warmup the application by hitting the same endpoints as you'd normally receive traffic on: during this warmup phase it would be "acceptable" to block exceptionally, as it's never going to block when under production load even though it might block once on first call.

So if you want to go this path, please ensure people can turn it off if they want to.



It won't catch lazy loading issues, but it will catch other blocking usage of the EM. 

I know about that one, but that's only one of the many ways to get a Session.

My point was that 
 A) we can add more thorough checks (e..g including lazy loading, but not only)
 B) we'll still have to accept we can't catch them all - especially for the many other libraries we integrate with
 C) some such use cases are valid and shouldn't be prevented
 

 


Stuart
 

Thanks,
Sanne



 

Stuart
 

Thanks


Since our user are using our live coding facilities they see this right away, and say we make the error better “Did you forget to say @Blocking on your resource class or method?”

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAD%2BL2czo_wHrTB-s89xP6_mDX0%3Dv3uiiH_jpByDNaRr34oeZig%40mail.gmail.com.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Paul Carter-Brown

unread,
Jun 28, 2021, 6:37:47 AM6/28/21
to Sanne Grinovero, Stuart Douglas, Sanne Grinovero, Jason T. Greene, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Quarkus Development mailing list, Loïc MATHIEU
Is it possible with bytecode manipulation or a runtime agent to decorate common blocking calls in java.io and java.nio and java.util.concurrent to catch all these occurrences?


Jason Greene

unread,
Jun 28, 2021, 8:38:52 AM6/28/21
to Sanne Grinovero, Stuart Douglas, Jason T. Greene, Guillaume Smet, Alexey Loubyansky, Georgios Andrianakis, Quarkus Development mailing list, Loïc MATHIEU
> On Jun 28, 2021, at 5:28 AM, Sanne Grinovero <sa...@hibernate.org> wrote:
>
> B) we'll still have to accept we can't catch them all - especially for the many other libraries we integrate with

Right the fallback is the Vert.x time check. Things can slip through,
but since a dev workflow (or a testsuite) is exercising multiple in an
interaction, in practice it will lead to the message.

Jason Greene

unread,
Jun 28, 2021, 9:19:58 AM6/28/21
to clement....@gmail.com, rene....@gmail.com, Quarkus Development mailing list
If we wanted to make sure they have decided something, we could make the Quarkus rest extension agnostic. So basically, you have to explicitly declare NonBlocking or Blocking in one of the following places:

1. Application - All JAX-RS methods in the app follow the setting (unless overridden)
2. Resource class - all resource methods on the class follow the setting (unless overridden). Overrides 1 for that class.
3. Method - apply this policy to just this method or override the default specified in to or 3

Then we can throw an error at build time if any resource method is not specified one way or the other. Additionally, we could make the code generator specify a default of blocking like Stuart’s option 3, but I kinda prefer generating it on the resource class since then it’s not “hidden”; it’s right in front of you.

IMO I think that would be better than defaulting to Blocking, although it’s a subtle difference from the existing approach. 

You bring up a great point that we need to be thinking about the whole programming model across extensions. In some cases, we have an API split, so it’s not an issue in those cases (e.g., hib reactive vs. hib). In other instances, we have stuff that is purely reactive and should default that way. SmallRye reactive messaging for example.  We definitely need to preserve our opinion there.


On Jun 27, 2021, at 12:47 PM, clement escoffier <clement....@gmail.com> wrote:



Stuart Douglas

unread,
Jun 28, 2021, 7:50:44 PM6/28/21
to Greene, Jason, clement escoffier, rene....@gmail.com, Quarkus Development mailing list
On Mon, 28 Jun 2021 at 23:20, Jason Greene <jason....@redhat.com> wrote:
If we wanted to make sure they have decided something, we could make the Quarkus rest extension agnostic. So basically, you have to explicitly declare NonBlocking or Blocking in one of the following places:

1. Application - All JAX-RS methods in the app follow the setting (unless overridden)
2. Resource class - all resource methods on the class follow the setting (unless overridden). Overrides 1 for that class.
3. Method - apply this policy to just this method or override the default specified in to or 3

Then we can throw an error at build time if any resource method is not specified one way or the other. Additionally, we could make the code generator specify a default of blocking like Stuart’s option 3, but I kinda prefer generating it on the resource class since then it’s not “hidden”; it’s right in front of you.

I think for a lot of applications this will just be boilerplate cruft though, if I am writing a blocking application having to put @Blocking everywhere is at best extra boilerplate, and at worst a source of bugs (even though we try and detect when methods block the IO thread, it's definitely not perfect).

Stuart
 

Martin Kouba

unread,
Jun 29, 2021, 1:31:51 PM6/29/21
to sdou...@redhat.com, Greene, Jason, clement escoffier, rene....@gmail.com, Quarkus Development mailing list
On 29. 06. 21 1:43, Stuart Douglas wrote:
>
>
> On Mon, 28 Jun 2021 at 23:20, Jason Greene <jason....@redhat.com
> <mailto:jason....@redhat.com>> wrote:
>
> If we wanted to make sure they have decided something, we could make
> the Quarkus rest extension agnostic. So basically, you have to
> explicitly declare NonBlocking or Blocking in one of the following
> places:
>
> 1. Application - All JAX-RS methods in the app follow the setting
> (unless overridden)
> 2. Resource class - all resource methods on the class follow the
> setting (unless overridden). Overrides 1 for that class.
> 3. Method - apply this policy to just this method or override the
> default specified in to or 3
>
> Then we can throw an error at build time if any resource method is
> not specified one way or the other. Additionally, we could make the
> code generator specify a default of blocking like Stuart’s option 3,
> but I kinda prefer generating it on the resource class since then
> it’s not “hidden”; it’s right in front of you.
>
>
> I think for a lot of applications this will just be boilerplate cruft
> though, if I am writing a blocking application having to put @Blocking
> everywhere is at best extra boilerplate, and at worst a source of bugs
> (even though we try and detect when methods block the IO thread, it's
> definitely not perfect).

Another option is to make it configurable, i.e. something like
quarkus.rest.methods-blocking-by-default=true/false. Yes, it's not ideal
and might be confusing as well but the problem with blocking-first and
non-blocking-first is that no matter what approach we'll choose there
will be plenty of users who won't be thrilled with the choice.

This way a user can decide what the default behavior of the application
is. We could also add some nice prompt in the CLI when the app is
created and explain the possibilities. Furthermore, the CLI can even
transform existing classes if needed, i.e. if a user decides at some
point to switch the default behavior, e.g. that the app should be
blocking by default (was non-blocking), we could remove @Blocking
annotations and add @NonBlocking annotations automatically...

>
> Stuart
>
>
> IMO I think that would be better than defaulting to Blocking,
> although it’s a subtle difference from the existing approach.
>
> You bring up a great point that we need to be thinking about the
> whole programming model across extensions. In some cases, we have an
> API split, so it’s not an issue in those cases (e.g., hib reactive
> vs. hib). In other instances, we have stuff that is purely reactive
> and should default that way. SmallRye reactive messaging for
> example.  We definitely need to preserve our opinion there.
>
>
>> On Jun 27, 2021, at 12:47 PM, clement escoffier
>> <clement....@gmail.com <mailto:clement....@gmail.com>>
>> wrote:
>>
>> 
>> Yes, letting the user decide would be best. Isn’t it that
>> different from what we have today?
>>
>> As a reminder, It’s not limited to RestEasy, but several other
>> extensions are involved (it also means that the Application class
>> cannot be used). If we decide to go in this direction, we need to
>> update several extensions and upstream projects. That is going to
>> take some time.
>>
>> If we do the change only for RestEasy, we need to rework some
>> integrations as the other extensions will offer a different
>> execution model, and the mix (calling one from the other) would
>> not be without consequences.
>>
>> Clement
>>
>> On Sun 27 Jun 2021 at 17:36, René Grob <rene....@gmail.com
>> <mailto:rene....@gmail.com>> wrote:
>>
>> Hi everyone,
>>
>> Why not letting the user decide? You could enforce that every
>> method needs to be configured to either be blocking or
>> non-blocking. Of course it's too cumbersome to do that on each
>> and every method but you could offer an optional global
>> default in application.properties (but if it is not set,
>> there's no default on that level), and another on
>> the Application class if present and then on class level, and
>> on method level. Like that the user needs to take a choice and
>> has full flexibility. Also there's no auto-magic involved
>> which can be as annoying as it can be helpful. Don't set a
>> default! Let the user set it or let the user decide on the
>> level he/she's comfortable with.
>>
>> WDYT?
>>
>> René
>>
>> On Friday, 25 June 2021 at 08:11:47 UTC+2 sdou...@redhat.com
>> <mailto:quarkus-dev...@googlegroups.com>.
>> <https://groups.google.com/d/msgid/quarkus-dev/7158ff83-4188-4fa1-a3bb-594c93ed19d7n%40googlegroups.com?utm_medium=email&utm_source=footer>.
>>
>> --
>> You received this message because you are subscribed to the Google
>> Groups "Quarkus Development mailing list" group.
>> To unsubscribe from this group and stop receiving emails from it,
>> send an email to quarkus-dev...@googlegroups.com
>> <mailto:quarkus-dev...@googlegroups.com>.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/quarkus-dev/CAKW6fif%2B-dGqJOuKQjcSwjZCrTZxA7fKTsw3LNpjWCs4CmJihA%40mail.gmail.com
>> <https://groups.google.com/d/msgid/quarkus-dev/CAKW6fif%2B-dGqJOuKQjcSwjZCrTZxA7fKTsw3LNpjWCs4CmJihA%40mail.gmail.com?utm_medium=email&utm_source=footer>.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Quarkus Development mailing list" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to quarkus-dev...@googlegroups.com
> <mailto:quarkus-dev...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/quarkus-dev/CAJMo4%2Bdo8JcCkMLykmuM0iJONTt6u%3DmcarSz718D32Tm%3DuS8zg%40mail.gmail.com
> <https://groups.google.com/d/msgid/quarkus-dev/CAJMo4%2Bdo8JcCkMLykmuM0iJONTt6u%3DmcarSz718D32Tm%3DuS8zg%40mail.gmail.com?utm_medium=email&utm_source=footer>.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Quarkus Development mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to quarkus-dev...@googlegroups.com
> <mailto:quarkus-dev...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/quarkus-dev/CAD%2BL2czvYBsud_rjXUCQ%3DCsc-PfxV7W_VjJdeWzRvEbaHBZAfA%40mail.gmail.com
> <https://groups.google.com/d/msgid/quarkus-dev/CAD%2BL2czvYBsud_rjXUCQ%3DCsc-PfxV7W_VjJdeWzRvEbaHBZAfA%40mail.gmail.com?utm_medium=email&utm_source=footer>.

--
Martin Kouba
Software Engineer
Red Hat, Czech Republic

Max Rydahl Andersen

unread,
Jul 5, 2021, 8:47:00 AM7/5/21
to Martin Kouba, sdou...@redhat.com, Greene, Jason, clement escoffier, rene....@gmail.com, Quarkus Development mailing list
Are we able to reach a conclusion on this ?

I'll admit I'm still leaning for blocking as default after reading the
full thread because Slow but working is generally better than Fast and
faulty.

/max
> an email to quarkus-dev...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/quarkus-dev/bb6963ae-f597-f531-7ab6-38beaf72035c%40redhat.com.

clement escoffier

unread,
Jul 5, 2021, 12:49:53 PM7/5/21
to Max Rydahl Andersen, sdou...@redhat.com, Greene, Jason, rene....@gmail.com, Quarkus Development mailing list, Martin Kouba
As you can imagine, I'm leaning towards the opposite.
The current default provides a better overall experience. Changing it will introduce multiple "default" execution models as soon as you combine resteasy reactive with gRPC, or messaging, or redis… 

Providing an option, that the user can consciously configure, would be more beneficial. 

Clement

Georgios Andrianakis

unread,
Jul 5, 2021, 1:18:24 PM7/5/21
to clement escoffier, Max Rydahl Andersen, Stuart Douglas, Greene, Jason, rene....@gmail.com, Quarkus Development mailing list, Martin Kouba


On Mon, Jul 5, 2021, 19:50 clement escoffier <clement....@gmail.com> wrote:
As you can imagine, I'm leaning towards the opposite.
The current default provides a better overall experience. Changing it will introduce multiple "default" execution models as soon as you combine resteasy reactive with gRPC, or messaging, or redis… 

Providing an option, that the user can consciously configure, would be more beneficial. 

+1

Max Rydahl Andersen

unread,
Jul 5, 2021, 1:52:00 PM7/5/21
to Georgios Andrianakis, clement escoffier, Stuart Douglas, Greene, Jason, rene....@gmail.com, Quarkus Development mailing list, Martin Kouba
On 5 Jul 2021, at 19:18, Georgios Andrianakis wrote:

> On Mon, Jul 5, 2021, 19:50 clement escoffier
> <clement....@gmail.com>
> wrote:
>
>> As you can imagine, I'm leaning towards the opposite.
>> The current default provides a better overall experience. Changing it
>> will
>> introduce multiple "default" execution models as soon as you combine
>> resteasy reactive with gRPC, or messaging, or redis…

>>
>> Providing an option, that the user can consciously configure, would
>> be
>> more beneficial.

And what is the suggestion for that option ?
Do you mean that the normal user will need to understand he should at
@Blocking ?

/max
>> https://groups.google.com/d/msgid/quarkus-dev/CAKW6fif0-e9Z4eL1B95uoTRTamQ77OD8eE6a__oTK6PcUYB%3D6Q%40mail.gmail.com
>> <https://groups.google.com/d/msgid/quarkus-dev/CAKW6fif0-e9Z4eL1B95uoTRTamQ77OD8eE6a__oTK6PcUYB%3D6Q%40mail.gmail.com?utm_medium=email&utm_source=footer>
>> .
>>

Stuart Douglas

unread,
Jul 5, 2021, 7:32:14 PM7/5/21
to clement escoffier, Max Rydahl Andersen, Greene, Jason, rene....@gmail.com, Quarkus Development mailing list, Martin Kouba
On Tue, 6 Jul 2021 at 02:50, clement escoffier <clement....@gmail.com> wrote:
As you can imagine, I'm leaning towards the opposite.
The current default provides a better overall experience. Changing it will introduce multiple "default" execution models as soon as you combine resteasy reactive with gRPC, or messaging, or redis… 

I don't think anyone was suggesting that we dynamically select the execution model based on installed extensions. The only 'dynamic' behaviour we have talked about is in code.quarkus.io selecting a default based on installed extensions.
 

Providing an option, that the user can consciously configure, would be more beneficial. 

We can already do that for resteasy-reactive via the application class. I don't think we need to update everything so that we have a single 'default execution model'. Even if a user wants RR to be blocking I don't think that would necessarily also apply to Reactive Messaging or gRPC.

Stuart

Jason Greene

unread,
Jul 5, 2021, 7:38:32 PM7/5/21
to Max Rydahl Andersen, Martin Kouba, sdou...@redhat.com, Greene, Jason, clement escoffier, rene....@gmail.com, Quarkus Development mailing list
As I have repeatedly stated, I think we can do *a lot* better then
picking a default that is bad for a significant number of use cases
(either default is bad). See the alternatives I keep putting forth.

> On Jul 5, 2021, at 7:46 AM, Max Rydahl Andersen <mand...@redhat.com> wrote:
>
> Are we able to reach a conclusion on this ?

Jason Greene

unread,
Jul 5, 2021, 7:58:46 PM7/5/21
to Jason Greene, Max Rydahl Andersen, Martin Kouba, sdou...@redhat.com, clement escoffier, rene....@gmail.com, Quarkus Development mailing list
One of them being the *already* shipped status quo (2 modules). We
could just back the blocking variant with resteasy reactive under the
hood.

> On Jul 5, 2021, at 6:38 PM, Jason Greene <jason....@redhat.com> wrote:
>
> As I have repeatedly stated, I think we can do *a lot* better then picking a default that is bad for a significant number of use cases (either default is bad). See the alternatives I keep putting forth.

Jason Greene

unread,
Jul 5, 2021, 8:04:54 PM7/5/21
to Stuart Douglas, clement escoffier, Max Rydahl Andersen, Greene, Jason, rene....@gmail.com, Quarkus Development mailing list, Martin Kouba


On Jul 5, 2021, at 6:31 PM, Stuart Douglas <sdou...@redhat.com> wrote:

Providing an option, that the user can consciously configure, would be more beneficial. 

We can already do that for resteasy-reactive via the application class.

IMO this is still a good option. 

I don't think we need to update everything so that we have a single 'default execution model'. Even if a user wants RR to be blocking I don't think that would necessarily also apply to Reactive Messaging or gRPC.


I agree that we  can do stuff in Application that is tied to JAX-RS only. I think Clement’s main point is the overall interaction of the APIs. If you are using Reactive Messaging as a client in a REST service, you would likely prefer non blocking all the way through. So we should think about the full programming model and side effects with setting any default model.




Stuart Douglas

unread,
Jul 5, 2021, 8:11:51 PM7/5/21
to Jason Greene, Max Rydahl Andersen, Martin Kouba, clement escoffier, rene....@gmail.com, Quarkus Development mailing list
I think the issue with the 'two modules' approach is the downstream modules, as you then need downstream modules for things like JSON etc (well you don't need them, but it makes for a more consistent experience).

At the moment if you include quarkus-resteasy-jsonb you get everything you need. If you just include quarkus-resteasy-reactive-jsonb you would get the non-blocking version, unless you also included quarkus-rest (or whatever we decide to call it).

I think having two modules will just end up causing more confusion, especially when it is basically just to represent a boolean value for the default (I think it is already confusing for the user to know which HTTP modules they want).

Stuart

Stuart Douglas

unread,
Jul 5, 2021, 8:24:08 PM7/5/21
to Jason Greene, clement escoffier, Max Rydahl Andersen, rene....@gmail.com, Quarkus Development mailing list, Martin Kouba
The client model is determined by the API signature and usage though. If you return Uni<T> it is non-blocking, if you return T it is blocking. I guess you could use it to determine which thread you use to invoke the Uni listeners (so you can do blocking stuff with the result), but I am not sure if people would be expecting that. I think that this really applies to cases where methods are invoked by the container, e.g. HTTP, gRPC and reactive messaging.

The more I think about it the more I think we should just make blocking the default:

- The majority of our users will use blocking IO
- A lot of those users will have no idea about IO threads and non blocking IO, and what this all means
- Users doing non-blocking IO should understand this, so we just note in the docs that they need to add an @NonBlocking Application class and they are good to go
- We can WARN on Uni<T> return types when the default is blocking and no explicit model has been specified for the class

The downside is backwards compatibility, however it's a very simple fix (just add an application class).

Stuart

Gavin King

unread,
Jul 6, 2021, 4:34:32 AM7/6/21
to Quarkus Development mailing list
On Friday, June 25, 2021 at 8:11:47 AM UTC+2 sdou...@redhat.com wrote:

1) Change the default to @Blocking, but in a smart way so we can still dispatch to @Async endpoints without switching to a worker thread


Sorry I'm late to this thread, but is there a really strong reason we can't just infer @Blocking if the method return type is anything other than Uni?
 

Georgios Andrianakis

unread,
Jul 6, 2021, 4:36:49 AM7/6/21
to Gavin King, Quarkus Development mailing list
Yes (and Micronaut went down that route and then backed out of it).
The basic reason is because you can never be sure just by looking at the return type.
Some users think that just wrapping a regular response in an async return type magically makes the whole thing run on the event loop.
Furthermore, returning a regular type doesn't mean anything blocked, you could just be returning some canned JSON response.

IMO, we should not go down the road of using the return type.
 

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Gavin King

unread,
Jul 6, 2021, 4:46:21 AM7/6/21
to Quarkus Development mailing list
On Tuesday, July 6, 2021 at 10:36:49 AM UTC+2 gand...@redhat.com wrote:

The basic reason is because you can never be sure just by looking at the return type.

Well then we should design our contracts and programming model so you *can* be sure, no?
 
Some users think that just wrapping a regular response in an async return type magically makes the whole thing run on the event loop.

I don't understand this problem. On the contrary, it sounds like the behavior I would want, as described in your very next sentence:
 
Furthermore, returning a regular type doesn't mean anything blocked, you could just be returning some canned JSON response.

Then return it in a Uni. One function call.
 

Gavin King

unread,
Jul 6, 2021, 4:52:14 AM7/6/21
to Quarkus Development mailing list
I mean, you knew I was going to say this, because I always default to solving problems *using the type system*. Because I don't hate myself and my users.

Georgios Andrianakis

unread,
Jul 6, 2021, 4:52:56 AM7/6/21
to Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 11:46 AM Gavin King <gavin...@gmail.com> wrote:


On Tuesday, July 6, 2021 at 10:36:49 AM UTC+2 gand...@redhat.com wrote:

The basic reason is because you can never be sure just by looking at the return type.

Well then we should design our contracts and programming model so you *can* be sure, no?
 
Some users think that just wrapping a regular response in an async return type magically makes the whole thing run on the event loop.

I don't understand this problem. On the contrary, it sounds like the behavior I would want, as described in your very next sentence:

This questions is indicative of what I have heard plenty of times from users: https://stackoverflow.com/q/68229816/2504224

Imagine this case

@GET
public List<Fruit> get() {
return entityManager.createNamedQuery("Fruits.findAll", Fruit.class)
.getResultList();
}
 
What I am saying is that some users seem to think simply changing it to:

@GET
public Uni<List<Fruit>> get() {
return Uni.createFrom().item(entityManager.createNamedQuery("Fruits.findAll", Fruit.class)
.getResultList());
}

will give them the benefits of Reactive.

In Quarkus as we don't have a way to peek into the implementation of the method, we have no idea what the user is actually doing to return that Uni.
 
Furthermore, returning a regular type doesn't mean anything blocked, you could just be returning some canned JSON response.

Then return it in a Uni. One function call.

Sure, but why should I be forced to do that? 
 

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Georgios Andrianakis

unread,
Jul 6, 2021, 4:54:08 AM7/6/21
to Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 11:52 AM Gavin King <gavin...@gmail.com> wrote:
I mean, you knew I was going to say this, because I always default to solving problems *using the type system*. Because I don't hate myself and my users.

I know and I wish there were a consistent way to do this.
The only way I see is if Quarkus could indeed play the role of the compiler here and look into what the method implementation is actually doing.

On Tuesday, July 6, 2021 at 10:46:21 AM UTC+2 Gavin King wrote:
On Tuesday, July 6, 2021 at 10:36:49 AM UTC+2 gand...@redhat.com wrote:

The basic reason is because you can never be sure just by looking at the return type.

Well then we should design our contracts and programming model so you *can* be sure, no?

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Gavin King

unread,
Jul 6, 2021, 4:58:37 AM7/6/21
to Quarkus Development mailing list


On Tuesday, July 6, 2021 at 10:52:56 AM UTC+2 gand...@redhat.com wrote:
What I am saying is that some users seem to think simply changing it to ... will give them the benefits of Reactive. 

So the problem is that users think that even though WE HAVE A WHOLE PRODUCT CALLED HIBERNATE REACTIVE, they can get reactive by just calling  Uni.createFrom() on regular Hibernate.

I think it's clear that users who are that confused aren't really serious about reactive programming.

I don't think this is a serious objection, frankly.

 
In Quarkus as we don't have a way to peek into the implementation of the method, we have no idea what the user is actually doing to return that Uni.
 
Furthermore, returning a regular type doesn't mean anything blocked, you could just be returning some canned JSON response.

Then return it in a Uni. One function call.

Sure, but why should I be forced to do that? 

Because it explicitly indicates my intent, instead of making the framework guess what I mean based on garbage fragile heuristics.

 

Gavin King

unread,
Jul 6, 2021, 5:03:49 AM7/6/21
to Quarkus Development mailing list
On Tuesday, July 6, 2021 at 10:54:08 AM UTC+2 gand...@redhat.com wrote:

I know and I wish there were a consistent way to do this.
The only way I see is if Quarkus could indeed play the role of the compiler here and look into what the method implementation is actually doing. 

Gawd no. That would crazy-crazy, not brilliant-crazy.

Just ask the user to be explicit. Don't try to guess what they mean. That's at some level one of the very most basic principles of API design.

And, luckily for us, in 95% of cases, nonblocking methods *already* return Uni, so it's already explicit.

 

Georgios Andrianakis

unread,
Jul 6, 2021, 5:04:22 AM7/6/21
to Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 11:58 AM Gavin King <gavin...@gmail.com> wrote:


On Tuesday, July 6, 2021 at 10:52:56 AM UTC+2 gand...@redhat.com wrote:
What I am saying is that some users seem to think simply changing it to ... will give them the benefits of Reactive. 

So the problem is that users think that even though WE HAVE A WHOLE PRODUCT CALLED HIBERNATE REACTIVE, they can get reactive by just calling  Uni.createFrom() on regular Hibernate.

I think it's clear that users who are that confused aren't really serious about reactive programming.

I don't think this is a serious objection, frankly.

I don't agree. For Hibernate vs Hibernate Reactive sure, it should be very easy. But for other APIs, it's potentially not that obvious. 

This is also what Micronaut figured out the hard way. From their docs:

In Micronaut 1.x, Micronaut would attempt to automatically deduce the thread pool to use based on the return type of the controller method.

This approach caused some confusion and created a false association between reactive types and blocking operations.


 
In Quarkus as we don't have a way to peek into the implementation of the method, we have no idea what the user is actually doing to return that Uni.
 
Furthermore, returning a regular type doesn't mean anything blocked, you could just be returning some canned JSON response.

Then return it in a Uni. One function call.

Sure, but why should I be forced to do that? 

Because it explicitly indicates my intent, instead of making the framework guess what I mean based on garbage fragile heuristics.

 

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Georgios Andrianakis

unread,
Jul 6, 2021, 5:06:39 AM7/6/21
to Gavin King, Quarkus Development mailing list
We are in agreement, we just don't agree on what that intent is.
For me, the only pragmatic intent is the annotation.
If Java where new and async return types actually meant non-blocking IO, then yeah, the return type would be best

 

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Gavin King

unread,
Jul 6, 2021, 5:10:17 AM7/6/21
to Quarkus Development mailing list
On Tuesday, July 6, 2021 at 11:04:22 AM UTC+2 gand...@redhat.com wrote:

I don't agree. For Hibernate vs Hibernate Reactive sure, it should be very easy. But for other APIs, it's potentially not that obvious. 

Concrete example, please? Because I'm just not seeing it.

It seems kinda intuitively obvious that reactive operations are going to return a reactive stream ... because, well, *that's our whole reactive programming model*.

And if we have libs that are not fitting into that model, then that is a problem of the design of those libs and their (lack of) integration with the Quarkus programming model. (And *that* should be fixed.)
 
This is also what Micronaut figured out the hard way. From their docs:

In Micronaut 1.x, Micronaut would attempt to automatically deduce the thread pool to use based on the return type of the controller method.


So sounds like they got it right the first time, and then futzed it up. Silly them. Let's not reproduce their mistake.
 

This approach caused some confusion and created a false association between reactive types and blocking operations.

It's not a false association. It's correct.


Georgios Andrianakis

unread,
Jul 6, 2021, 5:14:14 AM7/6/21
to Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 12:10 PM Gavin King <gavin...@gmail.com> wrote:


On Tuesday, July 6, 2021 at 11:04:22 AM UTC+2 gand...@redhat.com wrote:

I don't agree. For Hibernate vs Hibernate Reactive sure, it should be very easy. But for other APIs, it's potentially not that obvious. 

Concrete example, please? Because I'm just not seeing it.

It seems kinda intuitively obvious that reactive operations are going to return a reactive stream ... because, well, *that's our whole reactive programming model*.

And if we have libs that are not fitting into that model, then that is a problem of the design of those libs and their (lack of) integration with the Quarkus programming model. (And *that* should be fixed.)
 
This is also what Micronaut figured out the hard way. From their docs:

In Micronaut 1.x, Micronaut would attempt to automatically deduce the thread pool to use based on the return type of the controller method.


So sounds like they got it right the first time, and then futzed it up. Silly them. Let's not reproduce their mistake.

Exactly the opposite in my book. They tried what you proposed, they had in production for the entirety of the 0.x and 1.x releases and it turned out to not work in practice. It's an example to learn from if anything the way I see it
 

This approach caused some confusion and created a false association between reactive types and blocking operations.

It's not a false association. It's correct.

In an ideal world, sure. In practice, it's not a ironclad rule


--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Georgios Andrianakis

unread,
Jul 6, 2021, 5:17:23 AM7/6/21
to Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 12:13 PM Georgios Andrianakis <gand...@redhat.com> wrote:


On Tue, Jul 6, 2021 at 12:10 PM Gavin King <gavin...@gmail.com> wrote:


On Tuesday, July 6, 2021 at 11:04:22 AM UTC+2 gand...@redhat.com wrote:

I don't agree. For Hibernate vs Hibernate Reactive sure, it should be very easy. But for other APIs, it's potentially not that obvious. 

Concrete example, please? Because I'm just not seeing it.

It seems kinda intuitively obvious that reactive operations are going to return a reactive stream ... because, well, *that's our whole reactive programming model*.

And if we have libs that are not fitting into that model, then that is a problem of the design of those libs and their (lack of) integration with the Quarkus programming model. (And *that* should be fixed.)
 
This is also what Micronaut figured out the hard way. From their docs:

In Micronaut 1.x, Micronaut would attempt to automatically deduce the thread pool to use based on the return type of the controller method.


So sounds like they got it right the first time, and then futzed it up. Silly them. Let's not reproduce their mistake.

Exactly the opposite in my book. They tried what you proposed, they had in production for the entirety of the 0.x and 1.x releases and it turned out to not work in practice. It's an example to learn from if anything the way I see it

Here is the issue that provides all the history of their decision: https://github.com/micronaut-projects/micronaut-core/issues/2215

Gavin King

unread,
Jul 6, 2021, 5:19:43 AM7/6/21
to Quarkus Development mailing list
On Tuesday, July 6, 2021 at 11:14:14 AM UTC+2 gand...@redhat.com wrote:

In an ideal world, sure. In practice, it's not a ironclad rule

We're the one's designing this API. We just declared it an ironclad rule.

There, done. Fixed. 

That's our job. We declare rules.
 

Gavin King

unread,
Jul 6, 2021, 5:21:29 AM7/6/21
to Quarkus Development mailing list
On Tuesday, July 6, 2021 at 11:17:23 AM UTC+2 gand...@redhat.com wrote:

Here is the issue that provides all the history of their decision: https://github.com/micronaut-projects/micronaut-core/issues/2215

I actually don't care at all what is the history of Micronaut's bad decisions.

I want to know: *why can't this work in Quarkus*.

And you have so far not given me a single concrete example that would break it.
 

Georgios Andrianakis

unread,
Jul 6, 2021, 5:22:34 AM7/6/21
to Gavin King, Quarkus Development mailing list
Absolutely, for our APIs. But we don't control what's come before, not the mental baggage people have accumulated over the years of Java development
 

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Georgios Andrianakis

unread,
Jul 6, 2021, 5:33:03 AM7/6/21
to Gavin King, Quarkus Development mailing list
So let me get this straight: You trust users to take a random library (say for example their favorite HTTP Client, or some NoSQL client) make their calls (which you can't know how long they will take, as there could be IO, there could be computation) and not wrap the return in a Uni thinking everything will be fine?
Well sorry, but having answered so many user questions, I don't have the same faith you do.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Georgios Andrianakis

unread,
Jul 6, 2021, 5:35:23 AM7/6/21
to Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 12:32 PM Georgios Andrianakis <gand...@redhat.com> wrote:


On Tue, Jul 6, 2021 at 12:21 PM Gavin King <gavin...@gmail.com> wrote:


On Tuesday, July 6, 2021 at 11:17:23 AM UTC+2 gand...@redhat.com wrote:

Here is the issue that provides all the history of their decision: https://github.com/micronaut-projects/micronaut-core/issues/2215

I actually don't care at all what is the history of Micronaut's bad decisions.

Because we can't learn from the mistakes of others? You know better than I do that that's not an epistemologically sound argument.

Gavin King

unread,
Jul 6, 2021, 5:36:30 AM7/6/21
to Quarkus Development mailing list
Look, AFAIK, Mutiny already provides packaged functions to convert any sort of reactive result type into a Uni.

It's not absurd to ask people to call one of these functions to convert things coming from other libs.

Indeed, the main competing option, which is to use an annotation to do the job, instead of calling a function, is, well, simply not better.

And I understand you want to be super-flexible, and work with what people are doing today, but ultimately that's not the philosophy we're supposed to be following here. We already agreed that Quarkus was supposed to have opinions, and our opinion on reactive is Mutiny.

Ultimately, asking people to use Mutiny is just asking them to do the thing that is going to make their lives easiest. 

And if I'm using Mutiny the most natural thing in the world is to say "if the endpoint returns a Uni it's nonblocking".

Paul Carter-Brown

unread,
Jul 6, 2021, 5:37:00 AM7/6/21
to Georgios Andrianakis, Gavin King, Quarkus Development mailing list
When building a framework, it's fairly straightforward to know whether the code one is calling blocks or not. That's the advantage of being close to the raw implementations. Just remember though that a lot of code written by end-users to run in Quarkus has no clue whether they are blocking or not. A simple function like getRandomNumer() in some library may block waiting for system entropy, or a call to get a configuration value may at boot time have a plugin that retrieves config from a database. You just don't know....

This whole model of just "knowing" whether your code will block or not at build time is flakey to say the least. The only thing that knows whether you are blocking or not is the eventloop at runtime based on timing of the loop. At that point, a decision needs to be taken as to how to deal with similar calls. I just don't see a way around doing something at runtime.


Gavin King

unread,
Jul 6, 2021, 5:39:57 AM7/6/21
to Quarkus Development mailing list
On Tuesday, July 6, 2021 at 11:33:03 AM UTC+2 gand...@redhat.com wrote:

So let me get this straight: You trust users to take a random library (say for
example their favorite HTTP Client, or some NoSQL client) make their calls 
(which you can't know how long they will take, as there could be IO, there 
could be computation) and not wrap the return in a Uni thinking everything 
will be fine? 

If those libraries are nonblocking then they will return something like a CompletionStage of have some sort of callback or something when Mutiny knows how to wrap as a Uni.

Yes, I absolutely think it's reasonable to ask them to call a function to convert the result to Uni.
 
I think that's *totally* reasonable.

Georgios Andrianakis

unread,
Jul 6, 2021, 5:41:10 AM7/6/21
to Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 12:36 PM Gavin King <gavin...@gmail.com> wrote:
Look, AFAIK, Mutiny already provides packaged functions to convert any sort of reactive result type into a Uni.

It's not absurd to ask people to call one of these functions to convert things coming from other libs.

Indeed, the main competing option, which is to use an annotation to do the job, instead of calling a function, is, well, simply not better.

And I understand you want to be super-flexible, and work with what people are doing today, but ultimately that's not the philosophy we're supposed to be following here. We already agreed that Quarkus was supposed to have opinions, and our opinion on reactive is Mutiny.

I totally agree with this. I for one definitely want Quarkus to have strong opinions. I just don't want those opinions to clash hardly with the real world.

Ultimately, asking people to use Mutiny is just asking them to do the thing that is going to make their lives easiest. 

For sure, that is why Mutiny is pervasive throughout our APIs :)

And if I'm using Mutiny the most natural thing in the world is to say "if the endpoint returns a Uni it's nonblocking".


On Tuesday, July 6, 2021 at 11:22:34 AM UTC+2 gand...@redhat.com wrote:

Absolutely, for our APIs. But we don't control what's come before, not the mental baggage people have accumulated over the years of Java development 

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Gavin King

unread,
Jul 6, 2021, 5:41:59 AM7/6/21
to Quarkus Development mailing list
On Tuesday, July 6, 2021 at 11:35:23 AM UTC+2 gand...@redhat.com wrote:
On Tue, Jul 6, 2021 at 12:32 PM Georgios Andrianakis <gand...@redhat.com> wrote:

Because we can't learn from the mistakes of others? You know better than I do that that's not an epistemologically sound argument.

As I emphasized above, I would love to see a concrete example of the code you think would break this approach. An example from *Quarkus*, not from some other completely different ecosystem.

 

Gavin King

unread,
Jul 6, 2021, 5:43:31 AM7/6/21
to Quarkus Development mailing list
On the contrary, you *do always know* because nonblocking code doesn't return a result synchronously.

The nonblockingness is *always* visible in the function return type.

John O'Hara

unread,
Jul 6, 2021, 5:44:52 AM7/6/21
to Stephane Epardaud, Paul Carter-Brown, Georgios Andrianakis, Erin Schnabel, Jason Greene, Quarkus Development mailing list, kristij...@gmail.com
A bit late to this discussion, but wanted to add that the mean response times reported by wrk tool are not comparable between runs at different levels of throughput. Not only does wrk suffer from the co-ordinated omission problem (meaning response times are pretty much meaningless from an end-users perspective) but you are measuring 2 applications at the point they are 100% saturated, at different levels of load. Therefore direct comparisons of mean response time are not like-for-like.  
It also misses tail latencies and how an application degrades as it approaches maximum throughput.  In a real-world scenario, from a users perspective, the responsiveness of the system will be greater than 8ms.

On Mon, Jun 28, 2021 at 9:01 AM Stephane Epardaud <stephane...@gmail.com> wrote:


On Sat, 26 Jun 2021 at 16:49, Paul Carter-Brown <pa...@ukheshe.com> wrote:
So the question I ask myself is this - would a developer who does not know or care about eventloops and blocking and reactive and worker pools, care about 8 microseconds? 

Well, 8ms doesn't seem like much, but it's still a 34% drop in perf: all alone it's peanuts, but under load it's a huge number.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.


--

John O'Hara

Principal Software Engineer, Middleware (Performance)

Red Hat

Georgios Andrianakis

unread,
Jul 6, 2021, 5:45:45 AM7/6/21
to Gavin King, Quarkus Development mailing list
@GET
@Path("/random")
public Integer random() {
return randomService.get();
}
 How do I know if this "blocks" or not? As Clement says all the time, "blocking" is subjective to the system you are developing. If you are developing something that needs extremely high scalability, you can't get away with this call taking 100ms to complete as it will cause a build up of requests on the event loop thread.

 

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Georgios Andrianakis

unread,
Jul 6, 2021, 5:47:58 AM7/6/21
to Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 12:45 PM Georgios Andrianakis <gand...@redhat.com> wrote:


On Tue, Jul 6, 2021 at 12:42 PM Gavin King <gavin...@gmail.com> wrote:
On Tuesday, July 6, 2021 at 11:35:23 AM UTC+2 gand...@redhat.com wrote:
On Tue, Jul 6, 2021 at 12:32 PM Georgios Andrianakis <gand...@redhat.com> wrote:

Because we can't learn from the mistakes of others? You know better than I do that that's not an epistemologically sound argument.

As I emphasized above, I would love to see a concrete example of the code you think would break this approach. An example from *Quarkus*, not from some other completely different ecosystem.

@GET
@Path("/random")
public Integer random() {
return randomService.get();
}
 How do I know if this "blocks" or not? As Clement says all the time, "blocking" is subjective to the system you are developing. If you are developing something that needs extremely high scalability, you can't get away with this call taking 100ms to complete as it will cause a build up of requests on the event loop thread.

Now I understand your point that depending on what I am doing, I should or should not use a reactive return type. I just don't see how that is any better than using an annotation

Georgios Andrianakis

unread,
Jul 6, 2021, 5:51:03 AM7/6/21
to Gavin King, Quarkus Development mailing list
Another case, in point:

What happens if I use an async return type to hold the result of a CPU bound operation? If the framework sees the async type and thinks it should execute the method in the event-loop, then I am in for big trouble

Paul Carter-Brown

unread,
Jul 6, 2021, 5:53:05 AM7/6/21
to gavin...@gmail.com, Quarkus Development mailing list
This does not block. Never will:
public int add(int a, int b) {
return a+b;
}

So are we expecting developers to rewrite this as:

public Uni<Integer> add(int a, int b) {
return Uni.createFrom().item(a + b);
}



Gavin King

unread,
Jul 6, 2021, 5:54:17 AM7/6/21
to Quarkus Development mailing list


> How do I know if this "blocks" or not? 

It is *obviously* not reactive, since it returns Integer. It's *quite clearly* blocking. (By which I mean, it blocks the client while it does its work.)

Now, if I want to run it on the event loop *even though it is blocking* then I can easily do that, by calling Uni.createFrom().

This is, of course, an example of something that's rather rare in the kind of programs we deal with in Quarkus: it's an endpoint that does no IO of any sort. That's <<5% of endpoints.


Gavin King

unread,
Jul 6, 2021, 5:55:39 AM7/6/21
to Quarkus Development mailing list
*Absolutely* it blocks.

The client thread has to wait while it does its work.

That's just what "block" means.

Stuart Douglas

unread,
Jul 6, 2021, 5:58:39 AM7/6/21
to Georgios Andrianakis, Gavin King, Quarkus Development mailing list
On Tue, 6 Jul 2021 at 19:33, Georgios Andrianakis <gand...@redhat.com> wrote:


On Tue, Jul 6, 2021 at 12:21 PM Gavin King <gavin...@gmail.com> wrote:


On Tuesday, July 6, 2021 at 11:17:23 AM UTC+2 gand...@redhat.com wrote:

Here is the issue that provides all the history of their decision: https://github.com/micronaut-projects/micronaut-core/issues/2215

I actually don't care at all what is the history of Micronaut's bad decisions.

I want to know: *why can't this work in Quarkus*.

And you have so far not given me a single concrete example that would break it.
 
So let me get this straight: You trust users to take a random library (say for example their favorite HTTP Client, or some NoSQL client) make their calls (which you can't know how long they will take, as there could be IO, there could be computation) and not wrap the return in a Uni thinking everything will be fine?
Well sorry, but having answered so many user questions, I don't have the same faith you do.

By that logic we should not provide @NonBlocking either, it's just as big a foot gun.

Stuart
 

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/18b1a957-486b-49c2-98f2-050e872aa19en%40googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Gavin King

unread,
Jul 6, 2021, 5:58:49 AM7/6/21
to Quarkus Development mailing list
On Tuesday, July 6, 2021 at 11:53:05 AM UTC+2 Paul Carter-Brown wrote:

So are we expecting developers to rewrite this as:

public Uni<Integer> add(int a, int b) {
return Uni.createFrom().item(a + b);
}


Yes, precisely. (Assuming that you want to run a+b on the event loop, and not on a worker thread.)

There's just no way we can "infer" that a purely in-memory operation is safe to run on the event loop.

OK, a+b is fine.

But what if my in-memory operation is an n-body simulation that models galaxy formation from initial cosmic density perturbations?

In that case, it's certainly *not* safe to run it on the event loop.

So defaulting to worker thread is in a sense the "safest" default.

Georgios Andrianakis

unread,
Jul 6, 2021, 6:00:00 AM7/6/21
to Gavin King, Quarkus Development mailing list
Look, don't get me wrong, I absolutely understand where you are coming from.

But it's just that in my eyes, there is just too much subtle ambiguity in the (otherwise clean) model of using the return type

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.

Georgios Andrianakis

unread,
Jul 6, 2021, 6:01:05 AM7/6/21
to Stuart Douglas, Gavin King, Quarkus Development mailing list
On Tue, Jul 6, 2021 at 12:58 PM Stuart Douglas <sdou...@redhat.com> wrote:


On Tue, 6 Jul 2021 at 19:33, Georgios Andrianakis <gand...@redhat.com> wrote:


On Tue, Jul 6, 2021 at 12:21 PM Gavin King <gavin...@gmail.com> wrote:


On Tuesday, July 6, 2021 at 11:17:23 AM UTC+2 gand...@redhat.com wrote:

Here is the issue that provides all the history of their decision: https://github.com/micronaut-projects/micronaut-core/issues/2215

I actually don't care at all what is the history of Micronaut's bad decisions.

I want to know: *why can't this work in Quarkus*.

And you have so far not given me a single concrete example that would break it.
 
So let me get this straight: You trust users to take a random library (say for example their favorite HTTP Client, or some NoSQL client) make their calls (which you can't know how long they will take, as there could be IO, there could be computation) and not wrap the return in a Uni thinking everything will be fine?
Well sorry, but having answered so many user questions, I don't have the same faith you do.

By that logic we should not provide @NonBlocking either, it's just as big a foot gun.

It is, but it's rarely if ever used as far as we have seen.

Stuart Douglas

unread,
Jul 6, 2021, 6:01:34 AM7/6/21
to Paul Carter-Brown, Gavin King, Quarkus Development mailing list
In this case you would use @NonBlocking. This is about providing sensible defaults, not forcing an approach. Most endpoints talk to external systems, they don't just add two numbers together.

Stuart

Gavin King

unread,
Jul 6, 2021, 6:02:20 AM7/6/21
to Quarkus Development mailing list
Actually I think I see where the disconnect here is.

I speculate that you guys are assuming that non-IO operations can always be run on the event loop, is that right?

So I don't think that's correct.

If you're calling a microservice to do an in-memory operation, it quite likely it does something complex and expensive. Otherwise you would just do "a+b" locally. You wouldn't do a HTTP call.

I'm not saying that there are no cases where in-memory operations can be done on the event loop; clearly there are cases. But I don't think it's safe to assume that *by default*.
It is loading more messages.
0 new messages