I am (re-)opening a new discussion as this one is quite old but I am facing a similar issue I think.
Context:
- Node-RED version : 2.0.6
- NodeJS version: 16
I have developed a custom node which contains one input and 2 outputs.
One of the output is used to send the results of the input commands and the other one is used to send updates received from an internal callback method.
① The input -> output[1] seems to work as expected all the time.
② About the callback, it is invoked at a regular interval (~15 every seconds).
In this callback the node.send() function is used to send the messages to the second output.
=> So far so good. After a clean restart of Node-RED or even after doing some redeployment of the flow the messages are well received in the callback function and sent to the second output. I can verify it in the debug node.
But .. I have realized monitoring the second output and the console logs (to trace the messages received in the callback) that the messages stop being sent in the Node-RED flow and I have some data gaps.
I can see the messages in the console so I am sure they are received inside the callback. But they are not sent to the debug node (connected to my custom node second output).
Basically what I am doing in the callback.
1 - I forge a new message
2 - I log it into the console for tracing in debug mode the message (using NR node.debug() helper function)
3 - I send the message using NR node.send()
This is working fine but after some time nothing is sent by the send() function.
I have no idea why after a couple of days working fine (but it can vary to less than a day or a couple of hours) the messages are not flowing in the Node-RED environment but are still traced in my console !!
Am I missing something fundamental is the way Node-RED messages should be sent in the context of a callback method ? Are there some cases the messages can be dropped by the node.send function ?
Thank you very much for any inputs that could help me to clarify how to troubleshoot and fix this issue as it is a blocking point now in our development.
Thank you for your feedback
I can't share the whole code as it is a large project but here are the main sections for this node.
I have stripped some parts that are not related to this issue.
My concern is about the node.server.evc.status(config.deviceid, (device_id, message) function.
module.exports = function (RED) {
const {
DeviceStatusCode,
} = require("../../lib/build/edgeview/DeviceStatus.js");
function DeviceNode(config) {
RED.nodes.createNode(this, config);
this.server = RED.nodes.getNode(config.server);
this.deviceid = config.deviceid;
this.password = this.credentials.devicepwd;
this.isReady = this.server.isReady;
this.isConnected = false;
this.isReseting = null;
this.nodeid = Math.floor(100000 * Math.random(10));
var node = this;
...
this.connect = () => {
if (node.isConnected) return;
// subscribe (this comes from the config node)
node.server.evc
.initConnection(config.deviceid)
.then(() => {
node.log("🔗 connection established");
node.isConnected = true;
node.debug(
`Check 3 >> Connect attempt then check status again -> ${node.deviceid}:${node.nodeid} is connected on ${node.server.credentials.userid}:${node.server.environment.pubnub.channel} = ${node.isConnected}`
);
try {
node.getDeviceDetails(); // stripped in the extract
} catch (e) {
node.error("Cannot get the device details: " + e);
}
})
.catch((e) => {
node.error(`💀 initConnection failed -> ${JSON.stringify(e)}`);
node.status({ fill: "red", shape: "ring", text: "connection fail" });
//retry
if (this.isReseting === null) {
node.isReseting = setTimeout(() => {
node.warn(
`⛑ retry to initialize the connection for ${node.deviceid}...`
);
node.server.evc.bye(config.deviceid);
node.isConnected = false;
node.connect();
node.isReseting = null;
}, 20000);
}
});
node.server.evc.status(config.deviceid, (device_id, message) => {
let msg = {};
msg.topic = "device";
if (
message.code === DeviceStatusCode.ERROR ||
message.code === DeviceStatusCode.TOKEN_ERROR
) {
//retry
if (this.isReseting === null) {
node.server.evc.resetPamToken(node.deviceid)
node.isReseting = setTimeout(() => {
node.error(
"📩 😡 invalid message received: " +
JSON.stringify(message) +
" with code: " +
message.code
);
node.server.evc.bye(config.deviceid);
node.isConnected = false;
node.connect();
node.isReseting = null;
}, 30000);
}
}
node.debug("📩 new message received: " + JSON.stringify(message));
if (message.online) {
node.status({ fill: "green", shape: "ring", text: "online" });
if (this.isReseting !== null) {
clearTimeout(node.isReseting);
node.isReseting = null;
}
} else if (!message.online) {
node.status({ fill: "grey", shape: "ring", text: "offline" });
} else {
node.status({ fill: "red", shape: "ring", text: message.label });
}
msg.payload = {
device_id: device_id,
details: node.context().get("details") || {},
status: message.status,
};
node.send([msg, null]); // => this is where I am stucked..
});
...
};
...
this.server.statusMonitoring.on("emips-status", function (e) {
node.isReady = node.server.isReady;
node.log(
"📟 Device node " +
config.deviceid +
" has received: " +
JSON.stringify(e)
);
if (e.status === "ready" || e.status === "refresh") {
node.debug(`Check 1 >> Status of Egeview controller ${e.status}`);
node.debug(`Check 2 >> Status of node connection ${node.isConnected}`);
if (!node.isConnected) {
node.connect();
}
}
if (e.status === "error") {
if (this.isReseting === null) {
this.isReseting = setTimeout(() => {
this.node.warn(`⛑ reset connection for ${this.node.deviceid}...`);
node.server.evc.bye(device_id);
node.isConnected = false;
node.connect();
this.isReseting = null;
}, 10000);
}
}
});
// Triggered when an event comes on the input
this.on("input", function (msg, send, done) {
...
}
RED.nodes.registerType("device", DeviceNode, {
credentials: {
devicepwd: { type: "password" },
},
});
...
};
Also worth setting the debug node to output to the console to check that is not a browser or comms issue.
I am outputting the debug pane content also in a file (using flogger). So it is how I can see the gaps on the log run and analyze how much and how long the data loss is.
Are those lines the ones that show the debug in the log, but the message appears not to be sent?
I am a bit confused because you said that output 1 works correctly, but the send is sending to output 1.
One point to consider. If the code you showed is the callback then make sure you are not using the same msg object that is being handled by the rest of the node. Javascript accesses objects by reference so you could be overwriting an object that is also referenced elsewhere in the flow, which can give very odd symptoms. It is generally best to always create new message objects in asynchronous code.
Are those lines the ones that show the debug in the log, but the message appears not to be sent?
Yes, the "node.debug" always show the message properly in the console output. But at some point the "node.send" is not 'pushing' the message in the NR flow.
I am a bit confused because you said that output 1 works correctly, but the send is sending to output 1.
Sorry my bad indeed it is the first output that is used for the status updates and the second one for the commands.
One point to consider. If the code you showed is the callback then make sure you are not using the same msg object that is being handled by the rest of the node.
The msg object (that I am forging) scope is only in the callback and I am sending it only once. I've read some documentation about cloning the message to prevent the access by reference but I don't think it is the issue here as the object is not re-used or manipulated elsewhere..
OK, let us know what you find with the debug node set to output to console, as I suggested. Please also confirm that the debug node is definitely connected directly to the output.
A minor detail, the final else in the if (message.online) sequence will never be executed. Anything will always satisfy the if or the first else. If you want to test for boolean true and false, and anything else, then use ===true and ===false
I have just noticed that comment. Feed the node output direct into a debug node set to send to the console (and debug pane if you want). Then you can confirm definitely whether the message is being sent or not, without relying on anything else in the system.
I can confirm you what I observed previously.
This time I had a run for around 13hours without issue and then the logs coming from the debug node stop being present in the console but I still can see the logs written by the node.debug helper function.
Also as an additional comment this node is deployed multiple time in the flow and so I can also see differences between the nodes. They don't fail at the same time.
I join a screenshot to clarify it.
① In Node-RED nothing is shown anymore after around 13h
② But the logs created by the node.debug function in the callback are still written in the console
The node.sent function seems to fail at that point to push the messages.
I could fix this problem by changing the way the callback containing the node.send was registered.
It was most likely a bug on our code that let the callback being registered multiple times.
Still I don't understand the behavior of this node.send function in this context as I would have assumed the messages to be sent multiple time in the present case.