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

Don't block the event loop when reading inputstream. #6120

Merged
merged 6 commits into from
Sep 9, 2021

Conversation

graemerocher
Copy link
Contributor

@graemerocher graemerocher commented Sep 8, 2021

Fixes #6100

@graemerocher graemerocher changed the title Don't block the event loop when reading inputstream. Fixes #6100 Don't block the event loop when reading inputstream. Sep 8, 2021
@graemerocher graemerocher added the type: bug Something isn't working label Sep 8, 2021
@graemerocher graemerocher added this to the 3.0.2 milestone Sep 8, 2021
@@ -77,59 +93,61 @@ public InputStreamBodyBinder(HttpContentProcessorResolver processorResolver) {
PipedOutputStream outputStream = new PipedOutputStream();
try {
PipedInputStream inputStream = new PipedInputStream(outputStream);
processor.subscribe(new CompletionAwareSubscriber<ByteBufHolder>() {
Flux.from(processor)
.onBackpressureBuffer()
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't this lead to OOM?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

InputStream is not thread safe, so I don't see an alternative

Copy link
Contributor

Choose a reason for hiding this comment

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

Then I don't think this is an improvement

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Provide a reproducer for your OOM theory in that case. "I don't think" is not a valid argument opposing a bug fix

Copy link
Contributor

Choose a reason for hiding this comment

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

I will try to create the scenario. In the meantime, relax with your tone

@jameskleeh
Copy link
Contributor

Basically my thoughts are that if the stream is not read, etc.. we shouldn't buffer the data as that will lead to OOM exceptions. It's my understanding that in order to not buffer the event loop thread has to wait for the consumer to read the data.

@graemerocher
Copy link
Contributor Author

Feel free to amend the PR if you find a solution that is both thread safe and doesn't need buffering. Right now the current implementation is blocking the event loop

@jameskleeh
Copy link
Contributor

It's only blocking the event loop if the data isn't read. If the data isn't read with this change it could lead to OOM. I see this as a documentation issue

@graemerocher
Copy link
Contributor Author

graemerocher commented Sep 8, 2021

Also note that we are already buffering the data in FlowControlHandler so I don't buy the OOM argument, if you can provide a test where you can trigger an OOM then amend the test to demonstrate that.

@graemerocher
Copy link
Contributor Author

@jameskleeh that is not true, the test reads the data and it is blocking the event loop

@jameskleeh
Copy link
Contributor

Yes it is read after it requires a thread from the event loop. If it is read before that it would not be an issue

@graemerocher
Copy link
Contributor Author

Set a break point on the code that writes to the output stream and you will note that all writes to the output stream (a blocking API) occur on the event loop thread without my change. After my change they occur on the I/O thread.

If that isn't blocking the event loop I don't know what is 🤷‍♂️

@graemerocher
Copy link
Contributor Author

graemerocher commented Sep 8, 2021

Basically my thoughts are that if the stream is not read, etc.. we shouldn't buffer the data as that will lead to OOM exceptions. It's my understanding that in order to not buffer the event loop thread has to wait for the consumer to read the data.

I'm confused by what you are saying here, looking at the code prior to my change, the InputStream is bound and immediately on the same thread the code subscribes to the processor regardless whether any data is actually read from the InputStream:

PipedInputStream inputStream = new PipedInputStream(outputStream);
processor.subscribe(new CompletionAwareSubscriber<ByteBufHolder>() {

At this point the PipedOutputStream will be buffering the data anyway so I have no idea what you are referring to with regards to buffering being a problem with the change, since buffering is already happening within the PipedOutputStream

@jameskleeh
Copy link
Contributor

jameskleeh commented Sep 8, 2021

When you write to a PipedOutputStream, it blocks the current thread to wait for the reader to read the data. See the awaitSpace method in PipedInputStream

@graemerocher
Copy link
Contributor Author

Well exactly, so why are we writing to it on the event loop thread?

@jameskleeh
Copy link
Contributor

Perhaps I'm misunderstanding something here so please tell me if so

If we should not buffer data then the event loop thread has to wait for the data to be read before requesting more. If the server is continuously reading data from the client and the data isn't being read then it has to go somewhere (buffered), or block.

@graemerocher
Copy link
Contributor Author

Indeed you have to block somewhere, but that somewhere should not be the event loop which is what is currently being blocked.

@jameskleeh
Copy link
Contributor

jameskleeh commented Sep 8, 2021

I was able to reproduce the OOM in my example app before the latest changes

The request is hung with the latest changes (JDK 11). Could be some daemon/cache/etc issue on my machine

https://github.com/jameskleeh/delay-read-stream

Changing version back to 3.0.1 makes it work again

@jameskleeh jameskleeh merged commit 9de5f7b into 3.0.x Sep 9, 2021
@jameskleeh jameskleeh deleted the input-stream-deadlock branch September 9, 2021 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

@Body InputStream Blocks nioEventloop when its not read
2 participants