Weird delay output (empty message)

Got a weird error which I tracked down to delay node (configured as rate limit with delay). Not sure what is going on.

NR version: 4.1.1.
Node: 22

When sending flush control message, it also outputs the control message from the delay (with flush prop removed).

The message I define with flush includes _msgid and _event properties. But I didn't set this in the function node it originated from. I used this function node to return flush message back to queue: return { flush: 1 };.

Digged some more, and it seems to be affected by link in/out nodes. When function node use return {reset: 1}, it automagically adds _msgid prop. When it goes through link node, _event prop is added.

Seems like queue node can handle msg with reset, _msgid and _event. But when I try the same with flush, _msgid and _event, it treats it as both a flush control message and a new message. This removes the flush prop, but keeps _msgid and _event. Here is a simplified example:

[
    {
        "id": "afd7119ab90da785",
        "type": "inject",
        "z": "a7674a4aaf8a3c64",
        "name": "",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 1390,
        "y": 980,
        "wires": [
            [
                "bd046bd14e8ad79a"
            ]
        ]
    },
    {
        "id": "bd046bd14e8ad79a",
        "type": "function",
        "z": "a7674a4aaf8a3c64",
        "name": "flush",
        "func": "// reset any leftover in previous queue\nreturn { flush: 1 };",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1510,
        "y": 980,
        "wires": [
            [
                "1a5fb9a9de620724",
                "9ae07de9afcd7ef9"
            ]
        ]
    },
    {
        "id": "afa13d8bc425b20b",
        "type": "link in",
        "z": "a7674a4aaf8a3c64",
        "name": "link in 10",
        "links": [
            "1a5fb9a9de620724"
        ],
        "x": 1855,
        "y": 980,
        "wires": [
            [
                "7cd67bbf79c1dcad",
                "59bf2547ebe2d557"
            ]
        ]
    },
    {
        "id": "1a5fb9a9de620724",
        "type": "link out",
        "z": "a7674a4aaf8a3c64",
        "name": "link out 6",
        "mode": "link",
        "links": [
            "afa13d8bc425b20b"
        ],
        "x": 1615,
        "y": 980,
        "wires": []
    },
    {
        "id": "9ae07de9afcd7ef9",
        "type": "debug",
        "z": "a7674a4aaf8a3c64",
        "name": "debug 1: _msgid added",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1710,
        "y": 1020,
        "wires": []
    },
    {
        "id": "7cd67bbf79c1dcad",
        "type": "delay",
        "z": "a7674a4aaf8a3c64",
        "name": "queue",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "60",
        "rateUnits": "minute",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 2110,
        "y": 980,
        "wires": [
            [
                "8ddc534d989cc697"
            ]
        ]
    },
    {
        "id": "59bf2547ebe2d557",
        "type": "debug",
        "z": "a7674a4aaf8a3c64",
        "name": "debug 2: _evemt added",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 2030,
        "y": 1020,
        "wires": []
    },
    {
        "id": "91a80240032f6b46",
        "type": "inject",
        "z": "a7674a4aaf8a3c64",
        "name": "",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 1390,
        "y": 1080,
        "wires": [
            [
                "1729cb0bc398109f"
            ]
        ]
    },
    {
        "id": "1729cb0bc398109f",
        "type": "function",
        "z": "a7674a4aaf8a3c64",
        "name": "flush",
        "func": "// reset any leftover in previous queue\nreturn { flush: 1 };",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1510,
        "y": 1080,
        "wires": [
            [
                "ecbc8660ad045e8a",
                "97934838227f3705"
            ]
        ]
    },
    {
        "id": "ecbc8660ad045e8a",
        "type": "debug",
        "z": "a7674a4aaf8a3c64",
        "name": "debug 1: _msgid added",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1710,
        "y": 1120,
        "wires": []
    },
    {
        "id": "97934838227f3705",
        "type": "delay",
        "z": "a7674a4aaf8a3c64",
        "name": "queue",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "60",
        "rateUnits": "minute",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 2110,
        "y": 1080,
        "wires": [
            [
                "65c1572afd9a258f"
            ]
        ]
    },
    {
        "id": "65c1572afd9a258f",
        "type": "debug",
        "z": "a7674a4aaf8a3c64",
        "name": "queue output: none",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 2270,
        "y": 1080,
        "wires": []
    },
    {
        "id": "8ddc534d989cc697",
        "type": "debug",
        "z": "a7674a4aaf8a3c64",
        "name": "queue output: ghost message",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 2310,
        "y": 980,
        "wires": []
    }
]

Seems like queue node has a bug that treats control messages with _event prop as both control message and a new message for the queue?

Temp fix for me is to always add function node before queue node with this code:

const isControlMessage = msg.flush != null || msg.reset != null;
const hasEvent = msg._event != null;
if (isControlMessage && hasEvent) {
  delete msg._event;
}
return msg;

The flush message must be empty apart from the flush (and _msgid). The best way is to use a function node in the flush path, containing just
return {flush:1}
or similar.

2 Likes

Moving function node to the other side of the link (after link in) would work yeah. However then I need to stack multiple links there, one for flush, one for reset and so on. Right now all the flushes and resets are before the link out. Admittedly a bit of code duplication. But then I use the same function node for reset to reset multiple nodes (delay + join), so it can go both ways. Flush command must be empty, but reset doesn't need to be? It's a very easy trap to go into, when you don't know all places that add meta data to your messages.

I used to consider normal links as equivalent to "hidden" link in/out nodes. But to my surprise they are not interchangeable. For me, this was confusing as it caused a flow to crash. At least now I know and will take great care in future when dealing with link in/out nodes.

Perhaps too late now, but Node Red could benefit from separating msg into 2 parts: msg (as normal) and meta for meta data? This is how Thingsboard does it in their Rulechains. It's similar to Node Red, but in many other aspects much inferior.

What is a normal link?

1 Like

They are called wires? The lines or cables between nodes. Normally you start out with the visible ones. Later, I tried the more advanced hidden/invisble wires, they are dotted if you select an entry/exit node.

So are you saying you are seeing a difference between a wire and a link out/link in pair?
There is a difference with link call/return as it has to add the return meta data to the message, but I am not aware of anything similar with link out/in.

I have documented it here...

It does seem surprising that if you send a msg like this with no payload

{
"topic": "This is going on the queue", 
"flush": 3
}

Then the delay node (Not a queue node, which does exist but this isn't it) will flush three messages as well as adding the flush message to the queue.

I can see it might be useful in some circumstances though.

Sorry for confusing node name. At least you added the topic yourself.