Table of Contents
tldr; a node's runtime file (it's .js
file) needs to have a way to provide a callback function the runtime can call when the node module is being removed
With the palette editor making it easier to add/remove nodes from the editor, a gap has been revealed in this structure. Specifically, there is no hook in there for when the runtime wants to remove the module. If it has created any http route handlers (for example), they will be left behind.
Existing node runtime structure
Currently a node's runtime component is provided as a node.js module. This is loaded into the runtime using the standard require
function.
The node module is expected to have the basic structure:
module.exports = function(RED) {
}
The function it exports is called by the runtime, passing in the RED
object as a reference to the runtime api.
The function can optionally return a promise object that will resolve once the node module has been properly initialised. This is used if the node needs to take any asynchronous actions before it can decide if it's runtime dependencies are fully satisfied - for example, checking the presence of an external system.
var when = require('when');
module.exports = function(RED) {
return when.promise(function(resolve,reject) {
// initialise node module
// eventually either call resolve() if all is good,
// or call reject("my error message") if all is not good
});
}
Proposed changes
All of the following is optional - nodes will continue to work as written today
The exported function should now return a object that identifies additional configuration properties of the module. The only property this object may contain is the remove
function - to be called when the module is being removed from the runtime.
module.exports = function(RED) {
// Called when the module is loaded into the runtime.
// Initialise any state etc and register node constructors
return {
remove: function() {
// called when this module is being removed from the runtime
}
}
}
Alternatively, if a node needs to initialise asynchronously, it can return a promise (as before), that resolves to the config object:
module.exports = function(RED) {
// Called when the module is loaded into the runtime.
// Initialise any state etc and register node constructors
return when.promise(function(resolve,reject) {
resolve({
remove: function() {
// called when this module is being removed from the runtime
}
});
});
}
Other considerations
There are a number of initialisation actions a node may take today, such as registering its node types and adding an HTTP route handlers, amongst others.
As part of this work, we need to ensure every action can be undone.
There are two possible approaches. One is to provide suitable functions under RED
to undo any actions taken. That would allow actions to be undone at other times, not just unload.
The alternative is to wrap the functions provided by RED
to automatically record what actions are taken, and then automatically tidy them up when needed. That makes it easier for a node as it doesn't have to do anything explicit - in fact it would help to tidy up existing nodes that don't adopt this new scheme.
The options are not mutually exclusive. At a minimum, there should be functions to undo any action taken (registerType
being a possible exception). If the runtime also tidies up remaining resources, so much the better.
Action | Function | Reversed by |
---|---|---|
Register node | RED.nodes.registerType |
Handled by the runtime as part of the unload process |
Register http routes | RED.httpAdmin.get/post/etc |
Add RED.httpAdmin.remove function to undo them |
Register log handler | RED.log.addHandler |
(done) Add RED.log.removeHandler |