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
[Bug]: Electron 21 breaks node-api stability guarantees #35801
Comments
Correct, and modules do not need to be rebuilt to continue linking against / being loaded in Electron 21. That is the problem that Node-API was built to solve and we don't even claim to maintain that stability 🤷 Although I think we have purely by not-doing-the-wrong-thing. I don't think at any point the Node-API makes any claims about stability across different permutations of V8 flags (which technically could happen with third-party / different NodeJS builds too). It is relatively common behaviour if your application depends on a V8 flag in order to function it's implementation must be behind a A reasonable middle-ground is probably to expose a new symbol that returns whether the memory cage is enabled or not.
I think you're missing option 3 here which his by far the most ideal, allocate the memory in the cage and then write into it instead of allocate outside the cage and pointing a nodejs buffer at the external memory. In general I understand where you're coming from, especially as native module authors / maintainers but this decision was made considering the security of the ecosystem at large and one side of the equation would always be unbalanced. Either we'd be disabling an important new V8 security feature drastically deviating our security posture from Chromium or we'd be putting a subset (small by our math) of native modules into a non-working state. |
True, they do load but the behavioural differences would be classed a breaking change. Especially when it works differently to the version of node that is wrapped.
Yes you don't claim to maintain that, but you don't state either way so (I expect) it was widely assumed that you would do so to the benefit of the ecosystem. In fact your docs make no reference to node-api https://www.electronjs.org/docs/latest/tutorial/using-native-node-modules
But they have said "allow modules compiled for one major version to run on later major versions of Node.js without recompilation" and make very little reference to v8 in their docs. The node-api is designed to isolate modules from the v8 api, so why should I care about v8 flags? I have suspected for a while that nodejs are promising too much in this api being infinitely forwards compatible, but that is a different topic to take up with them.
Yes, but this is not always an option. (see below)
I do appreciate that this change is needed, my concern is that it comes as a surprise that many application developers and native module developers are going to be surprised by. It is very possible that I have missed some discussion that happened before the change was made, but I would definitely have had some input on how it should have been handled if I had seen anything sooner. A good case study for this concern is sharp. That module gets over 2 million installs from npm a week, and is affected by this. At the very least we can look at the short term, where copying will likely be necessary while refactoring is done to allow for more performant options. I suppose my key takeaway from this is that there is more Electron should have done, and more that you still can do. Was this discussed with the nodejs team at all? Perhaps this is something they should be considering doing in 19/20, and you could work together to phase out the methods from the api completely. It would have been much better to have a deprecation period of a release or two, with a warning written to the console when the method was called. This needs to be documented in the node-api docs, as that is the only documentation for this api. I would like to question the decision to make calls to this method crash the application. I would much prefer the buffer allocation to fail, rather than an application crash. At least that gives the user a chance of continuing to use the application rather than it crashing when they do something. |
This change could make upgrading to v21 extremely difficult for some applications to do before v20 goes out of maintenance, depending on the speed that the third-party module developers and the community can update their native module code. The planned breaking change was only documented about 75 days before it was implemented which isn't much lead time. I was not even aware of the planned breaking change until it was live - and it's a rather substantial one. |
👍 As maintainer of sharp I would love to continue to support Electron users, so the suggestion of providing a stable API to query for this feature and therefore the need to copy memory buffers would be really helpful. Thanks, as always, for maintaining Electron ❤️ |
@lovell Can you clarify (I can look through sharp too) how I'm wondering if an even better solution here (that I haven't even really explored technically but wondering what you think) would be to expose primitives for:
That have the exact same syntax as |
@MarshallOfSound It would be a bit of a hack, but preloading a shared library that provides I think I'd still prefer a stable API to detect the cage up-front rather than having the process crash. Failing that, we can add something explicit to the sharp API that allows someone developing for Electron 21+ to opt-in to copying the Buffer (and therefore opt-in to the performance and memory fragmentation cost). |
We discussed in the Node-API team meeting today and our current thought that we could:
@lovell would that provide a reasonable way for you to be able to detect up front? |
Hi - I'm also developing an electron app with a native component with off-heap buffers. I'm trying to figure out how/if to upgrade to Electron 21. Will the proposal
Be possible on any thread? |
@mhdawson A compile-time check that results in a status return at runtime (rather than the current process crash) sounds good to me, and is certainly enough to detect and workaround this change, thank you. At the |
@nkallen from our discussion in the node-api team meeting, instead of using malloc_in_v8_cage, free_from_v8_cage you can just allocate a buffer using
That should create the buffer in the right way and pass it back to you in the output data. |
@mhdawson and that needs to be done on the main js thread right? A common workflow requires doing work in a thread pool and allocating memory as a result of the computation. In my case I'm faceting CAD models into polygonal representations and need to send these 3d models to Electron/webgl. I'm thinking the best practice might be to pre-allocate a large buffer and maintain my own freelist or something. |
@nkallen yes I believe it would need to be called on the main thread. I agree your suggestion of pre-allocating one or more buffers and then managing re-use would make sense to me. |
OK thanks. I apologize for being negative, but I do want to note that this will add a significant amount of work for my project and I really wish Electron would reconsider the memory cage. |
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com>
I've created this PR to implement my suggestion above - nodejs/node#45181 |
I tested the PR above manually but if somebody could test in the context of electron that would be great. @MarshallOfSound, @lovell |
I agree that some workflows will make it impractical to preallocate a buffer. And I wouldn't be surprised that for sharp performing a This isn't a dealbreaker, but would be good to consider |
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: #45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: #45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: #45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: #45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: #45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: nodejs/node#45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: nodejs/node#45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: #45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Refs: electron/electron#35801 Refs: nodejs/abi-stable-node#441 Electron recently dropped support for external buffers. Provide a way for addon authors to: - hide the methods to create external buffers so they can avoid using them if they want the broadest compatibility. - call the methods that create external buffers at runtime to check if external buffers are supported and either use them or not based on the return code. Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: #45181 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Resubmitting my request here per Charles' (@ckerr) suggestion: Problem DescriptionIt's not uncommon for a desktop computer nowadays to have 64GB memory. There is a large array of desktop applications dealing with ML, image processing, rendering, e.g. displaying financial charts or annotation tools for medical images that require large datasets to be loaded in-memory and accessed for every mouse move (e.g. to display the precomputed inference of the hierachical bayesian network for the selected point in time, which is kept in a multi-gigabyte Float32Array).
There is no user code to run. Obviously, we do not include any hosted 3rd party content. All .js files limited to well-known frameworks (Vue3, react, etc) and vetted before getting incorporated into the code base and referenced directly. We do not benefit from the security benefits that you currently promote, it's simply unnecessary for us, it is the memory that the Renderer can immediately access we are after. I understand that maintaining such a build, especially with your insane pace of releases (we can only attempt to upgrade every few years, VSCode was behind for several years before they could catch up with you), would waste a lot of time unnecessarily. Proposed SolutionWould it be possible to provide an infrequent, e.g. once in 2-3 years, build without memory caging and, if possible, without memory compression? Alternatives ConsideredThere is a thread currently discussing the problem: #35241 Additional InformationWell, we actually love Electron overall, and we do want to continue using it going forward. Please help us leverage the entire available memory from the Renderer, it's vital to many applications. |
would be nice to see a recommended way to identify problematic libraries. luckily my issue was with zeromq, which was listed as fixed in this thread thank you @Bartel-C8 |
i've got this issue with 20.3.12 electron |
it present in electron 20 since #36626 (20.3.9 regarding patch notes)
so in addons builded with electron 20.3.9+ it's impossible to use external buffers |
Preflight Checklist
Electron Version
21
What operating system are you using?
Ubuntu
Operating System Version
Ubuntu 20.04
What arch are you using?
x64
Last Known Working Electron version
20
Expected Behavior
The NodeJS docs, state that the Node-API is stable api, intended so that modules do not need recompiling to work with newer version of NodeJS.
While I do not see Electron being referenced on that page, as Electron wraps specific versions of NodeJS and I have not seen reference to not following that stability guarantee, I would expect that Electron would follow the same Node-API support and stability promises as the version of NodeJS being wrapped.
At the very least, the NodeJS docs should be updated to make the methods deprecated or have a compatability warning, but ideally they shouldnt be broken at all.
Actual Behavior
In the blog post about enabling of v8 memory cages, it clearly states that "native modules which rely on this functionality in V8 will need to be refactored to continue working in Electron 20 and later.", which violates the Node-API stability.
The docs do say "This is a fairly rare use case", but out of the 5 native modules I have contributed to, all of them use this technique. So from my experience, every native nodejs module is now a liability.
https://www.npmjs.com/package/sharp is a good example of an incredibly popular library that breaks because of this
Testcase Gist URL
No response
Additional Information
I am now facing the dilemma of how to handle this breaking api issue. I could:
Of course, this doesnt help in cases where the module author is unaware of this electron incompatability, which I expect will be a lot of them. Without proper documentation in the node-api docs, how would they be expected to know?
I expect that this api breakage will cause users of them a lot of pain in transitioning to electron 21. This could end up with long chains of users waiting for dependencies of dependencies to update before they can update electron
The text was updated successfully, but these errors were encountered: