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

Memory leak in sink #6

Closed
betamos opened this issue Mar 28, 2020 · 3 comments · Fixed by #7
Closed

Memory leak in sink #6

betamos opened this issue Mar 28, 2020 · 3 comments · Fixed by #7

Comments

@betamos
Copy link

betamos commented Mar 28, 2020

It looks like sink has a memory leak. Repro:

const fs = require('fs')
const toIterable = require('stream-to-it')
const pipe = require('it-pipe')

async function* run() {
  for (let i = 0; i < 170000; i++) {
    yield Buffer.alloc(4096)
  }
}

(async () => {
  const sink = toIterable.sink(fs.createWriteStream('/some/outfile'))
  await pipe(run(), sink)
  console.log('done')
  await new Promise(resolve => setTimeout(resolve, 1000000));
})()

Memory consumption steadily increases until completion and stays at around 800-900 Mb. Total size of those buffers are around 700 Mb. Early diagnostics with chrome webtools point towards the promises and/or the callback hijacking of stream.end().

Found when using the upstream libp2p-js with libp2p-js-tcp.

node v13.11.0
Linux 4.15.0-91-generic #92-Ubuntu SMP Fri Feb 28 11:09:48 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

@betamos
Copy link
Author

betamos commented Mar 29, 2020

Played around a bit today and seems like the root cause is related to the V8 implementation (or even the spec according to the literals) of Promise.race. PTAL at streaming-iterables who worked around this very issue.

I confirmed that when substituting stream-to-it/sink with streaming-iterables/writeToStream, memory consumption was back to baseline (less than 20 Mb on my machine).

@alanshaw
Copy link
Owner

Thanks for the diagnosis @betamos - any ideas for how to fix?

@betamos
Copy link
Author

betamos commented Apr 6, 2020

I played around a bit with the code but didn't quite get it to work, so I ended up hot-swapping sink for streaming-iterables/writeToStream instead. I then used this in my libp2p stack and it didn't cause any problems so I called it a day.

I know that there are more features in this module than in writeToStream but I couldn't find much in terms of how they were actually supposed to work, so there was a good chance I'd break something upstream that depended on it.

I think the key is to create an internal source which yields both the messages from the original source and control messages (like error and finish), generated from plain old callbacks. It seems like one has to avoid Promise.race altogether under the current V8 implementation. I don't think it was misused or even used in an unusual fashion here.

alanshaw pushed a commit that referenced this issue Jul 13, 2020
Details for why the leak happens are in the issue, #6. This code is heavily influenced by the fix for writeToStream in streaming-iterables but avoids breaking any functionality here.

- Fix leak by removing Promise.race use
- Adds the [leak reproduction code](#6 (comment)) to a benchmarks folder. Run instructions leveraging Clinic.js are inline in the file. I can add to the readme if desired.
- Updates deps :)
- Added `package.files` to restrict what gets uploaded (tests were being packaged)
- Travis now runs Node.js `stable`(14) and `lts`(12)

## Clinic.js Reports

I've also listed the IPFS CIDs (linked to the gateway) if you want to peruse in detail.

**After** [bafybeidzy5otcicczh43ifdcg55btdl4q5j75vfolhsygr3oomqjavj6p4](https://gateway.ipfs.io/ipfs/bafybeidzy5otcicczh43ifdcg55btdl4q5j75vfolhsygr3oomqjavj6p4)
![image](https://user-images.githubusercontent.com/639834/87229565-4ef83480-c3a9-11ea-8b62-222c83931fa6.png)


**Before** [bafybeig3sw5t326is6e7rknf7ena3ffubekperpsdgf2dty7w74pkvmhai](https://gateway.ipfs.io/ipfs/bafybeig3sw5t326is6e7rknf7ena3ffubekperpsdgf2dty7w74pkvmhai)
![image](https://user-images.githubusercontent.com/639834/87229558-37b94700-c3a9-11ea-8b4a-9d5ca2318f14.png)

## Package file changes
```sh
$ npm pack --dry-run
npm notice 
npm notice 📦  [email protected]
npm notice === Tarball Contents === 
npm notice 1.1kB LICENSE     
npm notice 151B  duplex.js   
npm notice 215B  index.js    
npm notice 2.2kB sink.js     
npm notice 494B  source.js   
npm notice 395B  transform.js
npm notice 1.1kB package.json
npm notice 3.4kB README.md
```

fixes #6
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.

2 participants