Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support compression for reactive routes and resteasy reactive #24558

Merged
merged 1 commit into from
Mar 29, 2022

Conversation

mkouba
Copy link
Contributor

@mkouba mkouba commented Mar 25, 2022

Functionality:

  • nothing is compressed by default
  • if quarkus.http.enable-compression=true then an HTTP response body of a static resource, reactive route and resteasy-reactive resource method is compressed if:
    • the method is annotated with @io.quarkus.vertx.http.Compressed, OR
    • the content-type header is set and the value is listed in quarkus.http.compress-media-types (text/html,text/plain,text/xml,text/css,text/javascript,application/javascript by default).
  • a reactive route or resource method is never compressed if annotated with @io.quarkus.vertx.http.Uncompressed

Impl. notes:

  • if quarkus.http.enable-compression=true then every response has the Content-Encoding: identity header set by default; this effectively disables the compression
  • static resources, reactive routes and resteasy-reactive remove this header to enable the compression (rules described above)
  • resteasy-reactive removes the header in a special ServerRestHandler that is added to the chain for any resource method unless @Uncompressed is used
  • reactive routes use an end handler to remove the header if needed, unless @Uncompressed is used

Note that it's a breaking change in the sense that previously quarkus.http.enable-compression=true enabled compression for every HTTP response which is not the case anymore. The configuration property is effectively ignored for reasteasy-classic endpoints and routes registered manually.

@mkouba
Copy link
Contributor Author

mkouba commented Mar 25, 2022

What's missing:

  • more tests
  • docs
  • optimizations?

Copy link
Member

@cescoffier cescoffier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! I just had a few questions, but it looks great (exactly what I had in mind).

What I like is that it will ease the integration of other compression mechanism once they will be supported by the engine.

* List of media types for which the compression should be enabled automatically, unless declared explicitly via {@link Compressed} or {@link Uncompressed}.
*/
@ConfigItem(defaultValue = "text/html,text/plain,text/xml,text/css,text/javascript,application/javascript")
public Optional<List<String>> compressMediaTypes;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are this list also used when you return a response from resteasy reactive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should be. Although it's not tested yet. It's however not used for resteasy classic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's tested now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should honor this config property for reasteasy-classic or ignore it completely? Currently, reasteasy-classic has a separate config and annotation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now this config property and enableCompression is just ignored, i.e. it has no effect on http compression of resteasy classic endpoints.

/**
* The compression level used when compression support is enabled.
*/
public OptionalInt compressionLevel;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have both enableCompression and compressionLevel? Should we infer "enable" if the level is set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I don't think it's convenient to say: use quarkus.http.compression-level=6 to enable compression with the default compression level. In other words, enable=true should work in most cases and is simple and expressive.

@@ -12,6 +13,10 @@

Limits limits();

boolean enableCompression();

Set<String> compressMediaTypes();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it be compressed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, we have HttpConfiguration.compressMediaTypes so I just wanted to keep this aligned. HttpConfiguration.compressedMediaTypes is ok though. Should I rename both?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FTR I used the verb (compress) to keep consistency with enableCompression..

@mkouba mkouba force-pushed the issue-16425 branch 2 times, most recently from 171a9ba to c38c2bd Compare March 28, 2022 11:25
@mkouba mkouba requested review from cescoffier and geoand March 28, 2022 11:55

@Override
public void handle(RoutingContext context) {
context.addEndHandler(new Handler<AsyncResult<Void>>() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid that interception, we could imagine moving that code directly to the HTTP Server "root" request handler. Just an idea if we see a large decrease in terms of performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, so this handler should only be used if compression is enabled and I think that there will be other much more expensive calls in such case ;-)

@mkouba mkouba force-pushed the issue-16425 branch 2 times, most recently from 247e06b to ef05d35 Compare March 29, 2022 08:10
@mkouba
Copy link
Contributor Author

mkouba commented Mar 29, 2022

I believe that this PR is ready for review.

Note that it's a breaking change in the sense that previously quarkus.http.enable-compression=true enabled compression for every HTTP response which is not the case anymore. The configuration property is effectively ignored for reasteasy-classic endpoints and routes registered manually.

@mkouba mkouba marked this pull request as ready for review March 29, 2022 08:14
@mkouba mkouba requested review from cescoffier and geoand March 29, 2022 08:15
@mkouba mkouba added this to the 2.9 - main milestone Mar 29, 2022
@mkouba mkouba changed the title WIP: Support compression for reactive routes and resteasy reactive Support compression for reactive routes and resteasy reactive Mar 29, 2022
Copy link
Contributor

@geoand geoand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mkouba
Copy link
Contributor Author

mkouba commented Mar 29, 2022

The DevConsoleConfigEditorBodyHandlerTest.testChangeHttpRoute fails with java.lang.NoSuchMethodException: java.util.OptionalInt.<init>(). It seems to be a bug in io.quarkus.runtime.configuration.ConfigInstantiator.

@quarkus-bot

This comment has been minimized.

@mkouba
Copy link
Contributor Author

mkouba commented Mar 29, 2022

The DevConsoleConfigEditorBodyHandlerTest.testChangeHttpRoute fails with java.lang.NoSuchMethodException: java.util.OptionalInt.<init>(). It seems to be a bug in io.quarkus.runtime.configuration.ConfigInstantiator.

Ah, HttpConfiguration#compressionLevel is missing the ConfigItem annotation!

@mkouba
Copy link
Contributor Author

mkouba commented Mar 29, 2022

@FroMage @gsmet could you pls take a look at the functionality description and share your opinion? ;-)

@gsmet
Copy link
Member

gsmet commented Mar 29, 2022

Sorry I was busy on other stuff.
I think it makes perfect sense and this is really great.

TBH, I'm on the verge of including it for 2.8.0.Final if we get it merged before this evening. But maybe we can backport it later once people ask for it.

@mkouba
Copy link
Contributor Author

mkouba commented Mar 29, 2022

Sorry I was busy on other stuff. I think it makes perfect sense and this is really great.

No problem. I was just curious if it meets your expectations.

But maybe we can backport it later once people ask for it.

+1

@geoand
Copy link
Contributor

geoand commented Mar 29, 2022

But maybe we can backport it later once people ask for it.

👍🏼

@mkouba mkouba added the triage/waiting-for-ci Ready to merge when CI successfully finishes label Mar 29, 2022
@cescoffier
Copy link
Member

It's great!

@mkouba mkouba merged commit 1c9dbbd into quarkusio:main Mar 29, 2022
@quarkus-bot quarkus-bot bot removed the triage/waiting-for-ci Ready to merge when CI successfully finishes label Mar 29, 2022
@FroMage
Copy link
Member

FroMage commented Mar 31, 2022

@FroMage @gsmet could you pls take a look at the #24558 (comment) and share your opinion? ;-)

That's just awesome, bravo!

@geoand
Copy link
Contributor

geoand commented Apr 15, 2022

Coming back to this: Is there a good reason for making compression a runtime property?
If it were a build time property, we could not add the handler at all (which would be a tiny boost for performance as there would be one less call-site and one less volatile field read)

@cescoffier
Copy link
Member

@geoand speaking a bit about the future. At the moment, we only support one compression algorithm. This "set" will soon be expanded, and the other algorithms may (very likely) require native bits. We would need to pre-package the native bits (user config, of course). Following that logic, I would say that making it a build time property would make sense. If compression is disabled, it will avoid adding the native libs into the native executable and also tweak substitutions.

@geoand
Copy link
Contributor

geoand commented Apr 15, 2022

I agree. If @mkouba also agrees, I'll fix it and hopefully get the change in 2.8.1

@gsmet
Copy link
Member

gsmet commented Apr 15, 2022

Yeah it seems more reasonable to me too.

For now I haven’t backported this work. But we could backport it if we all agree it’s worth it.

@geoand
Copy link
Contributor

geoand commented Apr 17, 2022

Hm... I see that quarkus.http.enable-compression was not added by this PR, but has existed (as a runtime) property for a while...
So if we move it to a build time property, it will be a breaking change. Do we still want to do that (I am personally +1)?

@cescoffier
Copy link
Member

Yes, +1 too. As said, that change would happen eventually when we will be able to integrate Vert.x 4.3.

@geoand
Copy link
Contributor

geoand commented Apr 18, 2022

#24984 is the PR moving the configuration to build time

@mkouba
Copy link
Contributor Author

mkouba commented Apr 19, 2022

For now I haven’t backported this work. But we could backport it if we all agree it’s worth it.

I don't think it's a good idea to backport a breaking change.

Anyway, it probably makes sense to move the config property to the build time. Although it makes the config a bit inflexible, i.e. you'll need to rebuild your app in order to enable compression.

@geoand
Copy link
Contributor

geoand commented Apr 19, 2022

Anyway, it probably makes sense to move the config property to the build time. Although it makes the config a bit inflexible, i.e. you'll need to rebuild your app in order to enable compression.

Indeed, but what is the legitimate case for having differ between deployments of an artifact? I don't think there is one...

@mkouba
Copy link
Contributor Author

mkouba commented Apr 19, 2022

Indeed, but what is the legitimate case for having differ between deployments of an artifact? I don't think there is one...

I'm not following ;-). What I'm talking about is the use case where you build an app and in one deployment you want to enable compression because the data sent over the network are expensive and in other deployment the CPU is expensive but you don't care about the number of bytes sents. Right now, you can use the same app and change the config property. After this change, you'll have to build two versions of the app.

@geoand
Copy link
Contributor

geoand commented Apr 19, 2022

We are saying the same thing :)

What I am asking is if there a real scenario were one would actually do that...

@mkouba
Copy link
Contributor Author

mkouba commented Apr 19, 2022

What I am asking is if there a real scenario were one would actually do that...

I have no idea..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants