Closed NicolasCaous closed 5 years ago
Your steps to reproduce don’t work:
/home/pvo/workspace/js/clone/issue-106/test.js:18
await Dummy.sync();
^^^^^
SyntaxError: await is only valid in async function
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:139:10)
at Module._compile (module.js:616:28)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Function.Module.runMain (module.js:693:10)
at startup (bootstrap_node.js:188:16)
at bootstrap_node.js:609:3
I now changed your sample script to the following:
const Sequelize = require('sequelize');
const clone = require('clone');
async function test() {
const sequelize = new Sequelize(
'database-name',
'user',
'password',
{ host: 'ip', dialect: 'postgres' }
);
const Dummy = sequelize.define('dummy', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
unique: true,
type: Sequelize.BIGINT
}
});
await Dummy.sync();
const dummy = new Dummy({id: 1});
await dummy.save();
const clonedDummy = clone(dummy);
console.log(clonedDummy);
}
test();
This works flawlessly with Node.js v8.x, but doesn’t work with Node.js v10.16.11, failing with the following exception:
(node:18060) UnhandledPromiseRejectionWarning: TypeError: Cannot assign to read only property 'writeQueueSize' of object '#<TCP>'
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:16)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at _clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:162:18)
at clone (/home/pvo/workspace/js/clone/issue-106/node_modules/clone/clone.js:202:10)
at test (/home/pvo/workspace/js/clone/issue-106/test.js:24:25)
(node:18060) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:18060) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
So the root cause is clear. Sequelize opens a TCP connection, which, of course, cannot be cloned because it's a resource handle.
In order to make the object clonable, remove the reference to the TCP connection first, e.g. by deleting the _modelOptions
property on the dummy
object. Note that afterwards, you can no longer update and persist that object.
This code works:
const Sequelize = require('sequelize');
const clone = require('clone');
async function test() {
const sequelize = new Sequelize(
'clone_issue_106',
'postgres',
'postgres',
{host: 'localhost', dialect: 'postgres'}
);
const Dummy = sequelize.define('dummy', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
unique: true,
type: Sequelize.BIGINT
}
});
await Dummy.sync();
const dummy = new Dummy({id: 1});
await dummy.save();
delete dummy._modelOptions;
const clonedDummy = clone(dummy);
console.log(clonedDummy);
}
test();
I'm closing this, since I do not consider this a bug. Feel free to re-open if you do not agree.
Just checked with Node v12.7.0 and got the same FATAL ERROR: v8::Object::SetInternalField() Internal field out of bounds
as you did. While the error is different, the solution also works with Node v12.7.0.
@pvorb So, do you think it is an issue with clone (and therefore we should open it again) or should we investigate a little more and open a ticket elsewhere? I have never seen v8 cracking like this.
In a few days I will be able to investigate it further, but I want to know if you think it's worthwhile.
Well, in order to clone the TCP connection, clone
tries to set an internal field, which is not allowed. (This is probably because sequelize
uses the pg
module and pg-native
internally, which is written in C++ and maybe some protection mechanism prevents clone from setting a field.)
I don't think there is an issue with Node.js, since it is calling into native code. Native objects cannot be cloned using clone
, but it tries anyway because it cannot see the difference. The same is true for resources. The TCP connection from your code is both – a native object as well as a resource.
Just hit this same issue.
clone is being used by NodeRed (i.e. I have no control over it's use or the implementation).
In my case, including a ChildProcess object in a node-red message causes the process/V8 to die with
FATAL ERROR: v8::Object::SetInternalField() Internal field out of bounds
(node 12.10.0)
A variation of https://github.com/pvorb/clone/pull/93 could people to avoid this (kind of) error where they are not in control of the rest of the code which calls clone. I will propose a new PR.
reproduction:
var clone = require('clone');
var cp = require('child_process');
var spawn = cp.spawn;
var child = spawn('cmd', [], {});
var obj = {
child: child
};
var copy = clone(obj);
PR added for extending clone, and basic crash prevention.
Error
What I was trying to do
Inside
contextTree
there is a sequelize record instance. I was trying to clone it.What I have done to try to find the root cause
I isolated the error trying to clone its properties in isolation (
require('clone')(contextTree.a)
,require('clone')(contextTree.b)
, etc...) and could verify that the problem resides somewhere in the sequelize reference. However, I'm just a shitty developer without time to investigate more about its root causes. :(Steps to reproduce
Conclusion
The library shouldn't be liable to what is being cloned, but I find it bizarre that it breaks v8. I don't think it was suppose to do that even while cloning adverse and complex objects. There is something really wrong that I wasn't able to pin down. I think it is worth investigating.
ENV