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

buffer: do deprecation warning outside node_modules #19524

Closed
wants to merge 1 commit into from

Conversation

addaleax
Copy link
Member

@addaleax addaleax commented Mar 22, 2018

In addition to --pending-deprecation, emit a deprecation warning
for usage of the Buffer() constructor for call sites that are outside
of node_modules.

The goal of this is to better target developers, rather than
burdening users with an omnipresent and quickly ignored warning.

This implements the result of a TSC meeting discussion
from March 22, 2018.

Refs: #19079 (comment)

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

fyi @nodejs/collaborators

(Fwiw, I would still prefer to not do this and/or wait until after the Node 10 release.)

@addaleax addaleax added buffer Issues and PRs related to the buffer subsystem. semver-major PRs that contain breaking changes and should be released in the next major version. labels Mar 22, 2018
@addaleax addaleax added this to the 10.0.0 milestone Mar 22, 2018
@addaleax addaleax requested review from seishun and a team March 22, 2018 00:20
@nodejs-github-bot nodejs-github-bot added buffer Issues and PRs related to the buffer subsystem. util Issues and PRs related to the built-in util module. labels Mar 22, 2018
@addaleax addaleax added notable-change PRs with changes that should be highlighted in changelogs. and removed util Issues and PRs related to the built-in util module. labels Mar 22, 2018
@addaleax
Copy link
Member Author

addaleax commented Mar 22, 2018

@@ -93,6 +93,10 @@ is strongly recommended:
* [`Buffer.from(string[, encoding])`][from_string_encoding] - Create a `Buffer`
that copies `string`.

As of REPLACEME, a deprecation warnings is printed at runtime, when
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: "warning is" or "warnings are"?

@@ -315,6 +315,10 @@ It can be constructed in a variety of ways.
<!-- YAML
deprecated: v6.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/???
Copy link
Member

Choose a reason for hiding this comment

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

This can now be updated to 19524 (here and below).

const { runInNewContext } = require('vm');
// Use `runInNewContext()` to get something tamper-proof and side-effect-free.
// Since this is currently only used for a deprecated API, at most once
// per process, the perf implications should be okay.
Copy link
Member

@ChALkeR ChALkeR Mar 22, 2018

Choose a reason for hiding this comment

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

The problem here is that this is not called once per process. It's called once per every new Buffer() call until it finds a one that is not inside node_process.

How long would 1k / 10k calls take?
Perhaps, bail out after 1k/10k per process if no other suggestions?

Copy link
Member Author

Choose a reason for hiding this comment

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

Right – I switched to caching the function now.

I get ~ 16µs per call for this, so 10k calls would be 0.16 seconds.

(That’s a lot, yes.)

Copy link
Contributor

@seishun seishun left a comment

Choose a reason for hiding this comment

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

The goal of this is to better target developers, rather than burdening users with an omnipresent and quickly ignored warning.

There are many problems with this statement.

  1. We decided to deprecate the Buffer constructor because it's problematic, and the arguments for that are relevant regardless of where the code is located. Not showing the warning for call sites inside node_modules means hiding a (likely large) part of the problem.
  2. There is no evidence that a single-time warning is a significant burden for users, or that they "quickly ignore" it. At least I haven't seen such evidence having closely followed every Buffer-deprecation-related issue.
  3. Many modules are not actively tested on the latest Node.js versions, which means the problematic code in them will never be fixed, since no users will see and report the warning.
  4. Many applications and modules depend on old module versions, which means they'll continue running with problematic code even if all dependencies have been fixed long ago.

In addition, this behavior is surprising and inconsistent with other deprecation warnings. Imagine you're using Node.js for the first time, you copy some Buffer code from a module you're using (that doesn't print any warnings!) into your own code, and suddenly you get a warning. Then you spend a lot of time figuring out why it was not printed before, only to learn that it depends on the path to the file. That's not a great first impression.

@Trott
Copy link
Member

Trott commented Mar 22, 2018

@seishun You make some valid points. I'm only responding to the ones that I think could benefit from a broader context. I don't expect this to persuade you. Just informing the larger discussion.

There is no evidence that a single-time warning is a significant burden for users, or that they "quickly ignore" it. At least I haven't seen such evidence having closely followed every Buffer-deprecation-related issue.

As one of several people who have been running with export NODE_PENDING_DEPRECATION=1 set in my shell rc file, I can attest that the warnings end up everywhere for CLI tools. It is reasonable to conclude that most users will learn to ignore these messages completely.

Many modules are not actively tested on the latest Node.js versions, which means the problematic code in them will never be fixed, since no users will see and report the warning.

They will (hopefully) get fixed when the warnings are enabled for the contents of node_modules which will presumably happen in a later release.

Error.prepareStackTrace = function(err, trace) {
err.stack = trace;
};
Error.stackTraceLimit = Infinity;
Copy link
Member

@ChALkeR ChALkeR Mar 22, 2018

Choose a reason for hiding this comment

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

Infinity? Why? That would mean that a function that does new Buffer in a callback of e.g. express would not get the warning, correct? Shouldn't this check just the location of the file that does new Buffer()?

Copy link
Member

Choose a reason for hiding this comment

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

Ah, disregard this one comment, I think I understand how this works now, sorry =).
Not deleting just to be double-sure.

const { runInNewContext } = require('vm');
// Use `runInNewContext()` to get something tamper-proof and
// side-effect-free. Since this is currently only used for a deprecated API,
// the perf implications should be okay.
Copy link
Member

Choose a reason for hiding this comment

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

I still suggest bailing out after 10k attempts (which are about ~0.1 second).

Copy link
Member Author

Choose a reason for hiding this comment

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

@ChALkeR I think it might be helpful if you could explain why you are both very much pro-deprecation and in favor of moving all code off the deprecated API, but at the same time want to keep the performance on the same level as it was before?

Like @mafintosh said, isn't the perf hit a good incentive to move away?

Copy link
Member

@ChALkeR ChALkeR Mar 25, 2018

Choose a reason for hiding this comment

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

@addaleax Because, according to the numbers you presented, this change has the potential of silently adding from 10x to 100x synchronous performance hit on some workloads (for buffers of size 1 KiB), which could affect streams and stuff like that significantly.

Presenting that performance hit to people without any warning (so they have no idea what's going on) could be pretty disruptive and can cause issues with real deployments out there.

In my opinion, a deprecation warning would be less disruptive than a 100x silent performance hit.

@lpinca
Copy link
Member

lpinca commented Mar 22, 2018

They will (hopefully) get fixed when the warnings are enabled for the contents of node_modules which will presumably happen in a later release.

I'll repeat myself but in my opinion if we don't bug users and module authors in a way or another, nothing will be fixed and we'll just keep postponing the problem.

@mscdex
Copy link
Contributor

mscdex commented Mar 22, 2018

I agree with @lpinca

@bmeck
Copy link
Member

bmeck commented Mar 22, 2018

I have some concerns, we don't always develop our application code in a single top level package, we put various bootstrapping inside of node_modules including our server bootstrap. For cases where we have something like:

/app-logic
/app-logic/node_modules/server

Wouldn't this prevent deprecations coming from server which we update as a peer dependency essentially?

Also, wouldn't this cause problems if I had a parent directory containing node_modules such as /usr/local/lib/node_modules/ which is the default npm install -g directory on my machine? I would think running executables would be the most important place to give such deprecation.

@MylesBorins
Copy link
Contributor

@mafintosh Would the top level completely solve your problems? One thought I have is that if we still warn for top level modules, individual module test suites could still be broken by this. If the suites are broken by the warning, they will not be able to actively test against the latest version.

That being said, this approach is likely to have less people opening issues against the modules due to the warnings, meaning that the errors could be fixed when they are encountered... which is far less disruptive.

Thoughts?

@mafintosh
Copy link
Member

mafintosh commented Mar 22, 2018

@MylesBorins I think I'm 👎. The main thing I see this solving is stopping people writing new modules with new Buffer. As I understand it, that was one of the main TSC reasons for doing the runtime deprecation now. With the approach taken as is, in this PR, users writing new modules with new Buffer will immediately get hit by the deprecation warning (I'm open to even start throwing here at some point!). I think we should give this approach a chance to work. It's not like userland isn't updating as it is, it's just a very slow and looooong process.

@mafintosh
Copy link
Member

In addition the approach taken by @addaleax here has the added "benefit" of slowing down code running with new Buffer, which could be seen as an extra carrot for users to upgrade.

@MylesBorins
Copy link
Contributor

@mafintosh did you mean to 👍🏽?

To be more explicit, are you in support of this approach? I personally think it is a good idea, but just wanted to confirm that the above concern wasn't an issue

@mafintosh
Copy link
Member

@MylesBorins hehe sorry, I thought you asked me about @bmeck's comment. I'm 👍 on @addaleax's PR. I actually suggested this approach in the Buffer runtime thread

@addaleax
Copy link
Member Author

Also, wouldn't this cause problems if I had a parent directory containing node_modules such as /usr/local/lib/node_modules/ which is the default npm install -g directory on my machine? I would think running executables would be the most important place to give such deprecation.

@bmeck Yes. For the npm install -g use case, this is intended (at least by me) -- CLIs are almost never vulnerable to the actual issue, and since we're not talking about "real" breakage of a feature, there doesn't seem to be a point in warning users about it.

Regarding the other condition, intentionally calling a directory node_modules as part of code management: No idea what to do about it, tbh. I kind of hope that it's rare enough to not matter in the big picture of things.

// it's likely from Node.js core.
if (!/^\/|\\/.test(filename))
continue;
return /[\\/]node_modules[\\/]/.test(filename);
Copy link
Member

@mafintosh mafintosh Mar 22, 2018

Choose a reason for hiding this comment

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

if require.main.filename contains node_modules, we should ignore that node_modules folder here. I know plenty of devs (including myself), that use a node_modules folder to contain all the dev code to make cross module development easier.

Something like:

const nodeModulesParent = require.main.filename.indexOf('/node_modules/') // add windows support
function isNodeModules (filename) {
  const i = filename.lastIndexOf('/node_modules/')
  return i !== -1 && i > nodeModulesParent
}

This has the added benefit of making deprecations show if you cd into node_modules and run a test fx (i do that a bunch as well)

Copy link
Member Author

Choose a reason for hiding this comment

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

@mafintosh thanks, i'll look into it later, this makes sense!

I think we might adjust it abit because there might be different prefixes before /node_modules/ and we want to take care of backlashes as well, but generally this sgtm

Copy link
Member Author

Choose a reason for hiding this comment

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

@mafintosh updated, please take a look!

for (const frame of stack) {
const filename = frame.getFileName();
// If a filename does not start with / or contain \,
// it's likely from Node.js core.
Copy link
Member

Choose a reason for hiding this comment

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

Can you explain "it's likely"?

Copy link
Member Author

Choose a reason for hiding this comment

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

@benjamingr You could do the same thing we're doing here, run something inside a vm context with a specified filename.

If there are more such edge cases, I'm not aware of those, but I'm not comfortable claiming there aren't any either :)

// Match the prefix of the main file, if it is inside a `node_modules` folder,
// up to and including the innermost `/node_modules/` bit, so that
// we can later ignore files which are at the same node_modules level.
if (mainPrefix === undefined) {
Copy link
Member

Choose a reason for hiding this comment

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

👆 I little e.g. would help. like

e.g. 
`a/node_modules/b/node_modules/c/node_modules/d/d.js` would match 
`a/node_modules/b/node_modules/c/node_modules/`

@rvagg
Copy link
Member

rvagg commented Apr 3, 2018

Yeah, I think I can dig this, I like the 10k cutoff as a way to lean-in given the heavy-handed way the /node_modules/ check is done. Gives us another dial to turn to increase the pressure more slowly than a binary deprecation. It'll be interesting to see if this causes a hit on microbenchmarks that people will inevitably apply to Node 10 when it goes live. 🤞 that TF+I improvements give us an equivalent boost to make it disappear in the general case.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

LGTM

@Trott
Copy link
Member

Trott commented Apr 3, 2018

Given @seishun's objections in #19524 (review) and assuming they have not been addressed/answered, this cannot land without a TSC vote. So I'm going to put this on the agenda for tomorrow. If we're going to do this, we should decide that promptly. If we are not going to do it, we should close this and figure out what we are going to do.

@Trott Trott added the tsc-agenda Issues and PRs to discuss during the meetings of the TSC. label Apr 3, 2018
@BridgeAR
Copy link
Member

BridgeAR commented Apr 3, 2018

@seishun I know you want to have a stricter version than the suggestion here. Instead of the TSC vote, do you not maybe want to accept this PR and open a own PR afterwards to make the deprecation stricter? I suggest that mainly out of fear that we might end up with no deprecation message at all in 10.x if nothing lands soon :/.

@seishun
Copy link
Contributor

seishun commented Apr 3, 2018

@BridgeAR Judging from the TSC meeting discussion from March 22, 2018, the only reason why TSC might reject this PR (which seems unlikely given 5 approvals on behalf of TSC) is if they decide to land #15346 instead. So I would like to keep my objection explicit.

But I'll be sure to update #15346 accordingly once/if this lands. :)

Copy link
Member

@mhdawson mhdawson left a comment

Choose a reason for hiding this comment

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

LGTM

@addaleax
Copy link
Member Author

addaleax commented Apr 4, 2018

Copy link
Contributor

@MylesBorins MylesBorins left a comment

Choose a reason for hiding this comment

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

Rubber Stamp LGTM

@Trott
Copy link
Member

Trott commented Apr 4, 2018

TSC vote in the meeting today:

YES (10): @addaleax @Trott @targos @cjihrig @evanlucas @MylesBorins @mcollina @gibfahn @thefourtheye @ofrobots
NO (0)
ABSTAIN (0)

10 is enough to approve (19 members, so that's 50% + 1), so this can land.

@Trott Trott removed the tsc-agenda Issues and PRs to discuss during the meetings of the TSC. label Apr 4, 2018
In addition to `--pending-deprecation`, emit a deprecation warning
for usage of the `Buffer()` constructor for call sites that are outside
of `node_modules`.

The goal of this is to better target developers, rather than
burdening users with an omnipresent and quickly ignored warning.

This implements the result of a TSC meeting discussion
from March 22, 2018.

Refs: nodejs#19079 (comment)
PR-URL: nodejs#19524
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Rod Vagg <rod@vagg.org>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Apr 9, 2018
BridgeAR pushed a commit to BridgeAR/node that referenced this pull request Apr 9, 2018
In addition to `--pending-deprecation`, emit a deprecation warning
for usage of the `Buffer()` constructor for call sites that are outside
of `node_modules`.

The goal of this is to better target developers, rather than
burdening users with an omnipresent and quickly ignored warning.

This implements the result of a TSC meeting discussion
from March 22, 2018.

PR-URL: nodejs#19524
Refs: nodejs#19079 (comment)
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Rod Vagg <rod@vagg.org>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
@BridgeAR
Copy link
Member

BridgeAR commented Apr 9, 2018

Landed in 9d4ab90 🎉 🎉 🎉

@BridgeAR BridgeAR closed this Apr 9, 2018
addaleax added a commit to addaleax/node that referenced this pull request Apr 14, 2018
Taking the source code of a function and running it in another
context does not play well with coverage instrumentation.
For now, do the simple thing and just write the source code as
a string literal.

Fixes: nodejs#19912
Refs: nodejs#19524
@addaleax addaleax mentioned this pull request Apr 14, 2018
2 tasks
addaleax added a commit that referenced this pull request Apr 15, 2018
Taking the source code of a function and running it in another
context does not play well with coverage instrumentation.
For now, do the simple thing and just write the source code as
a string literal.

Fixes: #19912
Refs: #19524

PR-URL: #20035
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
jasnell pushed a commit that referenced this pull request Apr 16, 2018
Taking the source code of a function and running it in another
context does not play well with coverage instrumentation.
For now, do the simple thing and just write the source code as
a string literal.

Fixes: #19912
Refs: #19524

PR-URL: #20035
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@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
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. buffer Issues and PRs related to the buffer subsystem. notable-change PRs with changes that should be highlighted in changelogs. semver-major PRs that contain breaking changes and should be released in the next major version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.