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

http: Memory Leak #26957

Closed
ronag opened this issue Mar 28, 2019 · 12 comments
Closed

http: Memory Leak #26957

ronag opened this issue Mar 28, 2019 · 12 comments
Labels
http Issues or PRs related to the http subsystem. memory Issues and PRs related to the memory management or memory footprint.

Comments

@ronag
Copy link
Member

ronag commented Mar 28, 2019

Running this will cause the memory usage of the node process to infinitly grow...

  • Version: 10.13.0
  • Platform: OSX
  • Subsystem:
const http = require('http')
const { spawn } = require('child_process')
const crypto = require('crypto')
const fs = require('fs')

const dst = fs.createWriteStream('./tmp')

http.createServer((req, res) => {
  req
    .on('data', buf => {
      if (!dst.write(buf)) {
        req.pause()
      }
    })
  dst.on('drain', () => {
    if (req.readable) {
      req.resume()
    }
  })
}).listen(9988, () => {
  spawn('cat', ['/dev/zero'])
    .stdout
    .pipe(http
      .request({
        hostname: 'localhost',
        port: 9988,
        path: `/file`,
        method: 'POST'
      })
    )
})
@ronag ronag changed the title Memory Leak http: Memory Leak Mar 28, 2019
@ronag
Copy link
Member Author

ronag commented Mar 28, 2019

Backpressure seems to work, however the buf doesn't seem to be released. They seem to be allocated using some sort of external memory since they don't seem to be part of the node heap.

@addaleax addaleax added the memory Issues and PRs related to the memory management or memory footprint. label Mar 28, 2019
@ronag
Copy link
Member Author

ronag commented Mar 28, 2019

Interestingly using pipe instead of data & drain does not seem to have the same problem?

@ronag
Copy link
Member Author

ronag commented Mar 28, 2019

Seems like IncomingMessage does some strange stuff that pipe handles which is outside of the normal "stream protocol"?

@mcollina
Copy link
Member

Does this happen on the client side or on the server side?

@ronag
Copy link
Member Author

ronag commented Mar 28, 2019

@mcollina: server side

@ronag
Copy link
Member Author

ronag commented Mar 28, 2019

Seems related to chunked encoding

@addaleax addaleax added the http Issues or PRs related to the http subsystem. label Mar 28, 2019
@mcollina
Copy link
Member

@ronag are you working on a PR?

@ronag
Copy link
Member Author

ronag commented Mar 28, 2019

@mcollina no, I’m trying to find the problem but so far I have no idea. This might be over my head.

@ronag
Copy link
Member Author

ronag commented Mar 28, 2019

Our findings so far seem to indicate a difference between how Readable.resume and Readable.pipe resumes/pauses streams in a way where IncomingMessage and HttpServer depends on. In particular pipeOnDrain and resume seem to differ.

e.g. if instead of calling IncomingMessage.resume() I use the following code:

req._readableState.flowing = true
while (req.read() != null) {
}

I seem to no longer have any leaks. Though I don’t dare using such a workaround in production without understanding why that helps. My suspicion is the sync vs async resume behavior which is different between resume and pipeOnDrain.

addaleax added a commit to addaleax/node that referenced this issue Mar 28, 2019
`readable.resume()` calls `.read(0)`, which in turn previously set
`needReadable = true`, and so a subsequent `.read()` call would
call `_read()` even though enough data was already available.

This can lead to elevated memory usage, because calling `_read()`
when enough data is in the readable buffer means that backpressure
is not being honoured.

Fixes: nodejs#26957
@addaleax
Copy link
Member

@ronag Fwiw, I’ve opened a PR to address this in #26965. It would be great if you could verify that that fully works for you.

zbjornson pushed a commit to zbjornson/node that referenced this issue Mar 28, 2019
`readable.resume()` calls `.read(0)`, which in turn previously set
`needReadable = true`, and so a subsequent `.read()` call would
call `_read()` even though enough data was already available.

This can lead to elevated memory usage, because calling `_read()`
when enough data is in the readable buffer means that backpressure
is not being honoured.

Fixes: nodejs#26957
BethGriggs pushed a commit that referenced this issue Apr 5, 2019
`readable.resume()` calls `.read(0)`, which in turn previously set
`needReadable = true`, and so a subsequent `.read()` call would
call `_read()` even though enough data was already available.

This can lead to elevated memory usage, because calling `_read()`
when enough data is in the readable buffer means that backpressure
is not being honoured.

Fixes: #26957

PR-URL: #26965
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
BethGriggs pushed a commit that referenced this issue Apr 9, 2019
`readable.resume()` calls `.read(0)`, which in turn previously set
`needReadable = true`, and so a subsequent `.read()` call would
call `_read()` even though enough data was already available.

This can lead to elevated memory usage, because calling `_read()`
when enough data is in the readable buffer means that backpressure
is not being honoured.

Fixes: #26957

PR-URL: #26965
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Signed-off-by: Beth Griggs <Bethany.Griggs@uk.ibm.com>
BethGriggs pushed a commit that referenced this issue Apr 9, 2019
`readable.resume()` calls `.read(0)`, which in turn previously set
`needReadable = true`, and so a subsequent `.read()` call would
call `_read()` even though enough data was already available.

This can lead to elevated memory usage, because calling `_read()`
when enough data is in the readable buffer means that backpressure
is not being honoured.

Fixes: #26957

PR-URL: #26965
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Signed-off-by: Beth Griggs <Bethany.Griggs@uk.ibm.com>
@ronag
Copy link
Member Author

ronag commented Jun 1, 2019

@mcollina I'm a little surprised this wasn't fixed/merged in/into LTS? Is that on purpose?

@mcollina
Copy link
Member

mcollina commented Jun 3, 2019

@mcollina I'm a little surprised this wasn't fixed/merged in/into LTS? Is that on purpose?

I'm a little surprised of your question. There is no evidence of anybody blocking #26965 getting into Node 10. Likely, it seem they just need some help.

BethGriggs pushed a commit that referenced this issue Jul 9, 2019
`readable.resume()` calls `.read(0)`, which in turn previously set
`needReadable = true`, and so a subsequent `.read()` call would
call `_read()` even though enough data was already available.

This can lead to elevated memory usage, because calling `_read()`
when enough data is in the readable buffer means that backpressure
is not being honoured.

Fixes: #26957

PR-URL: #26965
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
BethGriggs pushed a commit that referenced this issue Jul 17, 2019
`readable.resume()` calls `.read(0)`, which in turn previously set
`needReadable = true`, and so a subsequent `.read()` call would
call `_read()` even though enough data was already available.

This can lead to elevated memory usage, because calling `_read()`
when enough data is in the readable buffer means that backpressure
is not being honoured.

Fixes: #26957

PR-URL: #26965
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http Issues or PRs related to the http subsystem. memory Issues and PRs related to the memory management or memory footprint.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants