I was thinking about this feature and it seems to make sense. What if we could create copies of nodes/subflows without actually making a new node instance when deployed? I saw people having issues with memory allocation when using subflows because when they duplicate a subflow node node-red creates new instances of all nodes used in that subflow, including one new instance for the subflow, even when the subflow doesn't contain any changes, and it is mainly used more as a DRY solution. I know that there are solutions for that such as converting your subflow into a new flow tab and use a link:call node. However, since copy by reference is a thing in OOP and nodes are kind of objects in memory, why can't we copy nodes as references too? This way I wont have to create tabs for my subflows and then add link:call nodes.
creating a copy by reference would be offered as a new shortcut and context menu button for nodes.
A node reference can be turned into its own instance with a shortcut and a new context menu button.
A node reference will have its config values and credentials changed when any of the references is eddited.
A node reference has a subtle icon design above it such as a & or * to remind us of pointers.
hovering the mouse over the reference icon will highlight all nodes that belong to the same reference. It is a view like when Users have to pick a node, but only the nodes that belong to the same references stay highlighted.
There is a new context menu button and shortcut to delete a node and all its references.
each node reference still has its own x,y,z,g,wires while the other props are shared between all references
all references share the same context store
I have several copies of the same function nodes and I don't need them to be new instances but just copies pointing to the same instance - references per say. I think this would help a lot to reduce memory consumption.
Example with subflow: Given a subflow x with 10 nodes inside. When the User places 2 copies of it in a canvas the number of nodes created are
COPY: 22 nodes (10+10+1+1)
COPY BY REFERENCE: 11 nodes (10+1) there is only one instance of the subflow in the runtime. This instance controls all "editor" instances props
Besides reducing the number of nodes in the runtime this feature can also get rid of using that virtual wire since now Users can just have a reference copy of the node they want to make connections closer to where it is needed, and then just use simple wires. It will reduce the amount of nodes and wires, since there is now no need to put link in and out int the canvas. Moreoever, it is important to highlight that it is way easier to read a flow when all nodes are close to each other, rather than following virtual wires that make the User's view transition to different positions in the canvas.
Each node is defined as an object. As soon as you make a copy several of the properties need to change - like x,y positions, id, and (as per your suggestion) the icon. So it is immediately not an exact copy so can’t be by reference. I don’t think it can be like overlay storage like docker uses that just stores differences. So not sure how this would ever work.
is it your birthday? that is why you have a cake icon on your name?
I think you guys can have a new id for "grouping" references together. A reference node is just a node that has 1 instance in the runtime. Its model would be somehting like this
All common props stay in the reference definition where particular ones in the node definition. This way the User could, for instance, move one node in the canvas without moving the other. In memory, all node references that are equal would be a single object, with a set of objects that contain the specific props of a reference.
class node {
constructor(nodes){
this.id = 1;
this.type = "function";
this.function = "return msg";
// NOTE: every object in this set holds only specifics for each reference (x, y, g, wires, z ...)
this.nodes = nodes;
// NOTE: add other props that are equal to all nodes
}
// TODO: add other methods to ease access to a specific node
// NOTE: common for all references
onInput(msg, send, done){}
// NOTE: common for all references
onClose(){}
}
Not sure what you are saving. Eg for that function the position is different, the name and id are different and the actual function is different. Ok so the icon and colour and type are the same but , and I think the possibility for “by reference” to catch people out unexpectedly (as indeed it does now in msgs) is far to great to outweigh any perceived benefit
If there are 10 'virtual' copies of a node deployed, and the node calls node.send(msg), which copy of the node sends the message?
An alternative approach to this problem is the idea of Singleton Subflow - GitHub · Where software is built - an idea that is nearing its 10yr anniversary...
But it still has some of the same technical challenges to figure out around how to decide which instance should send a message.
@knolleary
Each reference would still have their own wires, x, y, g. But context, type, config and credentials would all be managed separately. All references would be under the same object and when a message arrives in the reference, it must be dispatched to the wires connected to that reference only.
The references are a mean to edit multiple nodes's configs/credentials/context at the same time, and have a single object in memory controlling all of them, including routing. For example, when my function node A's code is changed in any of the references, all of the other ones will be changed too. I don't want to have to edit every single node or be forced to turn it into a subflow.
If a node calls node.send(msg), how does the runtime know which instance is doing that if they are all actually the same instance in the runtime.
If the node is using the newer messaging API (where the send function is passed in to the input event handler), then it might be possible. But there are plenty of nodes out there that don't.
I'd also be concerned about how understandable this would be to users. There are plenty of nodes that have internal state that a user may not understand that the state would now be shared between all reference instances. For example, a Join node would really not behave well in this mode.
From a user's perspective, this would be a third way to achieve the same sort of results you get with subflows and link call nodes. More choice isn't necessarily a good thing for a low-code environment.
Subflows were intended to be the mechanism for reusable components - edit once, apply everywhere. They aren't perfect, but I'd be more motivated to look at how they can be evolved as a concept.
Couldnt a User have 3 copies of the same subflow/node, 2 being from the same reference, and the other from a different one? The 2 that share the reference will share the context store, configs, credentials, and envs. While the other one will have its own context store, configs, redentials and env
EDIT:
I found a solution for the above scenario a user can create 2 subflows that use a third subflow where the common props can be placed.
However, I would still like to be allowed to make vrtual copies to stop using virtual wires. They break the flow of reading a flow because you have to move the canvas in different directions back and forth sometimes. Having all nodes that belong to the same flow close to each other and using an actual node would make the reading and interpretation of a flow much easier IMO. Instead of pushing a longer wire all the way to that particular node you need, or using virtual wires, or create a subflow, i could simply "double click on a node and select copy reference", then drag that copy to where I need. A virtual wire requires, at least, 2 extra nodes, while a virtual node would not require any extra nodes.
Im almost certain that being able to create virtual copies of a node, instead of using virtual wires, would make the UX and interpretation of flows much easier and natural. Do a quick simulation, like an AB test with unbiased Users to verify this hipothesis.
I also understand that virtual wires would not be fully deprecated, since they would still be needed to call nodes from different flow tabs.