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

zlib: only apply drain listener if given callback #3534

Closed
wants to merge 3 commits into from
Closed

zlib: only apply drain listener if given callback #3534

wants to merge 3 commits into from

Conversation

MylesBorins
Copy link
Contributor

When stream.flush() is called without a callback, an empty listener is
being added. Since flush may be called multiple times to push SSE's
down to the client, multiple noop listeners are being added. This in
turn causes the memory leak detected message.

fixes #3529

This commit has been cherry picked from a pr on v0.x-archive
nodejs/node-v0.x-archive#25679

@MylesBorins
Copy link
Contributor Author

I'm not sure what the official process is for grabbing commits from pr's from the archive.

I opted to cherry pick the commit and update the commit message, but retained the authorship with the original author.

@Trott
Copy link
Member

Trott commented Oct 26, 2015

Is there some way to add a test for this?

@MylesBorins
Copy link
Contributor Author

most likely. I'm more than happy to try and take on some of that work, but would love to see if this would be an acceptable solution before putting in time... especially since I'm not 100% on exactly how to test it yet :P

if (callback) {
this.once('drain', function() {
self.flush(callback);
});
Copy link
Member

Choose a reason for hiding this comment

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

I think I spot a bug here: the call should be self.flush(kind, callback), it forgets the kind now.

Also, I'd ES6 this up and write it as this.once('drain', () => this.flush(kind, callback)).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done.

@Fishrock123
Copy link
Contributor

I'm not sure what the official process is for grabbing commits from pr's from the archive.

This seems about right. :)

@MylesBorins
Copy link
Contributor Author

There were a few different things that have come in from the thread so I have split them up .

First was the bug that @bnoordhuis spotted where kind was not being to recursive calls of flush. This part should be able to be backported even if the rest of this PR is deemed semver breaking.

I also included a separate patch to explicitly check that callback is a function rather than just truthy. This was originally requested by @cjihrig in the original thread

This pattern does appear throughout the codebase in both ways... I'm going to make a separate issue about this.

It is also worth bringing up that @jasnell mentioned in the original thread that the zlib docs do not state that the callback is optional... so the docs may need to be updated.

@mscdex mscdex added the zlib Issues and PRs related to the zlib subsystem. label Oct 26, 2015
@MylesBorins
Copy link
Contributor Author

meta conversation about how callbacks are dealt with --> #3536

@cjihrig
Copy link
Contributor

cjihrig commented Oct 26, 2015

My personal opinion is that you should try to maintain the original author of the commit when cherry picking.

@@ -438,16 +438,15 @@ Zlib.prototype.flush = function(kind, callback) {
}

if (ws.ended) {
if (callback)
if (typeof callback ==='function')
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a space between === and 'function' throughout this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

got it

@bnoordhuis
Copy link
Member

Regression tests would be good.

My personal opinion is that you should try to maintain the original author of the commit when cherry picking.

I agree but 9d9f681 doesn't look like a cherry-pick to me.

@MylesBorins
Copy link
Contributor Author

@bnoordhuis I could have sworn I responded to this already 😵

that commit does differ from the original, but only in as far as changes were requested in this thread... seems like a fine line to me, so I opted to be conservative here

@MylesBorins
Copy link
Contributor Author

is the lack of a test the only thing holding this up atm?

@bnoordhuis
Copy link
Member

Yes. I'd drop 0865370 for now because it makes it semver-major instead of just a bug fix.

@MylesBorins
Copy link
Contributor Author

kk. I'll leave that to get cleaned up by #3539

@Fishrock123
Copy link
Contributor

@thealphanerd status of this?

@MylesBorins
Copy link
Contributor Author

@Fishrock123 planning to work on this today actually. Just have not had time to figure out how to test the change yet.

@bnoordhuis
Copy link
Member

@thealphanerd Status?

@MylesBorins
Copy link
Contributor Author

Still trying to wrap my head around Zlib, specifically the streams bit. Will focus on this today and hopefully have something tangible

@MylesBorins
Copy link
Contributor Author

I just pushed a first pass at doing a test.

  • an instance of deflator is created and forced into a drain state and flush is called.
  • It keeps track of the internal write stream of the zlib object, both when it needs to drain and once it has drained.
  • It assures that a drain event has been fired.
  • deflator.flush is shimmed to keep track of times it is executed, which is tested to ensure it has been executed twice

Now I'm not 100% that there is no way that calling flush right after write may have flush execute while it's internal write stream is not needing to drain. (Which would cause this test to fail)

If anyone has suggestions on how to improve this I am very interested. I would also be interested if anyone has a better practice for spying on functions as I did with the flush shim.

/cc @jasnell @bnoordhuis @chrisdickinson @Fishrock123

@MylesBorins
Copy link
Contributor Author

@MylesBorins
Copy link
Contributor Author

The failing CI bits seem unrelated to this PR to me

});

deflater.on('drain', function() {
drained = true;
Copy link
Member

Choose a reason for hiding this comment

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

If I understand the logic of this test right, I'd turn this into a counter and check that it's equal to flushCount on exit. If I don't understand the logic right, I'd still turn it into a counter and check that it has the expected value. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

drain should only execute once if I recall correctly, but I can double check that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok I've checked, and drain is only emitted once (as expected). That being said I will make it a counter rather than checking a boolean state

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is now fixed

Copy link
Member

Choose a reason for hiding this comment

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

I'm still seeing a boolean (or again? I could swear you'd made the changes.)

@jasnell
Copy link
Member

jasnell commented Nov 13, 2015

LGTM

@bnoordhuis
Copy link
Member

LGTM but can you run the CI again and would you mind fixing the spelling of my GH handle in the first commit?

@MylesBorins
Copy link
Contributor Author

Commit updated

new ci --> https://ci.nodejs.org/job/node-test-pull-request/722/

I'm more than happy to land this after the CI run, should this all be flattened to a single commit or is it alright to land as three?

Myles Borins and others added 2 commits November 13, 2015 14:19
When stream.flush() is called without a callback, an empty listener is
being added. Since flush may be called multiple times to push SSE's
down to the client, multiple noop listeners are being added. This in
turn causes the memory leak detected message.
MylesBorins pushed a commit that referenced this pull request Nov 17, 2015
Bug spotted by @bnoordhuis while doing code review on #3534

Refs: #3534 (comment)
PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Fishrock123 pushed a commit that referenced this pull request Nov 17, 2015
When stream.flush() is called without a callback, an empty listener is
being added. Since flush may be called multiple times to push SSE's
down to the client, multiple noop listeners are being added. This in
turn causes the memory leak detected message.

PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Nov 17, 2015
This test assures that if flush is called while the zlib object needs
to be drained that it will defer the callback until after the drain.

PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Dec 4, 2015
Bug spotted by @bnoordhuis while doing code review on #3534

Refs: #3534 (comment)
PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
rvagg pushed a commit that referenced this pull request Dec 4, 2015
When stream.flush() is called without a callback, an empty listener is
being added. Since flush may be called multiple times to push SSE's
down to the client, multiple noop listeners are being added. This in
turn causes the memory leak detected message.

PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Dec 4, 2015
This test assures that if flush is called while the zlib object needs
to be drained that it will defer the callback until after the drain.

PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
@jasnell jasnell mentioned this pull request Dec 17, 2015
MylesBorins pushed a commit that referenced this pull request Dec 17, 2015
Bug spotted by @bnoordhuis while doing code review on #3534

Refs: #3534 (comment)
PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
jasnell pushed a commit that referenced this pull request Dec 17, 2015
When stream.flush() is called without a callback, an empty listener is
being added. Since flush may be called multiple times to push SSE's
down to the client, multiple noop listeners are being added. This in
turn causes the memory leak detected message.

PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Dec 17, 2015
This test assures that if flush is called while the zlib object needs
to be drained that it will defer the callback until after the drain.

PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Dec 23, 2015
Bug spotted by @bnoordhuis while doing code review on #3534

Refs: #3534 (comment)
PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
jasnell pushed a commit that referenced this pull request Dec 23, 2015
When stream.flush() is called without a callback, an empty listener is
being added. Since flush may be called multiple times to push SSE's
down to the client, multiple noop listeners are being added. This in
turn causes the memory leak detected message.

PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Dec 23, 2015
This test assures that if flush is called while the zlib object needs
to be drained that it will defer the callback until after the drain.

PR-URL: #3534
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
zlib Issues and PRs related to the zlib subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

event emitter memory leak on flush
9 participants