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

Unnecessary blocking in ResourceService #5937

Closed
charlag opened this issue Feb 2, 2021 · 10 comments · Fixed by #5949
Closed

Unnecessary blocking in ResourceService #5937

charlag opened this issue Feb 2, 2021 · 10 comments · Fixed by #5949

Comments

@charlag
Copy link

charlag commented Feb 2, 2021

Jetty version
9.4.27.v20200227

Java version
14

OS type/version
GNU/Linux

Description
It seems Jetty is doing unnecessary blocking in ResourceService. There's a decision taken based on the content size:

https://github.com/eclipse/jetty.project/blob/b16313b1c89d86f025cbbeccf7aa1a8861788c57/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java#L686-L687

If the data size is smaller than buffer, it is still sent async but with blocking:
https://github.com/eclipse/jetty.project/blob/b16313b1c89d86f025cbbeccf7aa1a8861788c57/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java#L722
https://github.com/eclipse/jetty.project/blob/b16313b1c89d86f025cbbeccf7aa1a8861788c57/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java#L1136-L1143
(line is 1183 for the verison I report but I provide links to the current tree).

We tried to find out justification for doing that but we couldn't. As it's always sent async there's anyway a context switch. It also seems like an attack vector to starve thread pool by requesting small files.

We tried out version which always sends data async, independent of the size and didn't find any issues. As this looks like an oversight to us we would like to report this.

@joakime
Copy link
Contributor

joakime commented Feb 2, 2021

Have you changed your response buffer size?

At default value, if the content size is less than the buffer size, that sendContent should never block.
It's a simpler code path than the async one, with the same end result, send of the content without blocking.

@gregw Perhaps we should just skip the simpler code path if the response buffer size has been changed from default?

@sbordet
Copy link
Contributor

sbordet commented Feb 3, 2021

I agree with the OP that the if statement in ResourceService should be split.

@charlag would you like to provide a PR?

@charlag
Copy link
Author

charlag commented Feb 3, 2021

@sbordet we just removed the size conditon completely and as you mention "split" it seems like you have something else in mind.

@sbordet
Copy link
Contributor

sbordet commented Feb 3, 2021

@charlag yes I meant to remove the buffer size condition. @gregw do you recall why that check was added?

@joakime
Copy link
Contributor

joakime commented Feb 3, 2021

The ResourceService line for selecting async based on size came from a refactor to create the ResourceService in commit 460e6e2

Prior to that, the line in question was in the ResourceHandler.

https://github.com/eclipse/jetty.project/blob/7b713507c68a07c3031dfd1aa4077450cd271573/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java#L522-L528

Which was added by commit 0176856#diff-6762e0c2681e4060b6d130dcfb33f458af0ce52a9c033d0fc11036b4fb3c98dbR506

@gregw
Copy link
Contributor

gregw commented Feb 3, 2021

My recollection is that small content was not explicitly sent using asynchronous because it was aggregated into the output buffer, which could then asynchronously flushed. Also, small contents are less likely to block and can be entirely flushed within the call to sendContent, so the blocker may never block.

However, I'm not so sure that the response buffer size is a reasonable definition of "small content" now. Flip side is that I also don't think it is often worthwhile to startAsync just to send a few bytes of static content. Perhaps the HttpConfiguration._outputAggregationSize would be a better value to use here, short of making it directly configurable.

@charlag
Copy link
Author

charlag commented Feb 5, 2021

For us introduction of many small files resulted in exhausted thread pool (and downtime) with blocking in that place in HttpOutput. I checked and I didn't find us changing buffer size anywhere but I'm not 100% on this to be honest.

@gregw
Copy link
Contributor

gregw commented Feb 5, 2021

@charlag Interesting! Can you give us some numbers on "small" and "many" ?

I think it is a no-brainer that we should change this to HttpConfiguration._outputAggregationSize, second option is make it configurable and third option is always go async if possible. @sbordet @lorban thoughts?

@sbordet
Copy link
Contributor

sbordet commented Feb 5, 2021

@gregw IIUC this is either past the aggregation, which only happens in HttpOutput.write(), or a direct call to serve static content from e.g. DefaultServlet.

I fail to see why there is any logic in there about buffer sizing -- I would just remove the check.

gregw added a commit that referenced this issue Feb 5, 2021
Remove size limit on async static content.
@gregw gregw linked a pull request Feb 5, 2021 that will close this issue
@charlag
Copy link
Author

charlag commented Feb 5, 2021

@gregw with new release of https://mail.tutanota.com (still up) we had around 50 bundles, few kb each. As browsers started detecting the update files started being downloaded by few hundred thousand users at once.

I hope this is enough info, feel free to ask for more details.

gregw added a commit that referenced this issue Feb 5, 2021
updates from review.
This was referenced Mar 10, 2021
This was referenced Mar 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants