Delay node- rate limit extension

Say if;
The delay node is set to rate limit to 2 messages per every 8 seconds and drop messages.

If you send a message every 1 seconds it will send the first message and the 5th message. That's really not what I would expect. (That would be 1 message per every 4 seconds).

I would like to suggest that it be expanded / adjusted so the rate limit can send the first 2 messages, then the 9th and 10th message, etc, etc

01/01/2025, 13:56:50node: debug 4msg : Object
{ payload: 1735739809552, topic: "", _msgid: "d2cb56d8f4f82c5c" }
01/01/2025, 13:56:54node: debug 4msg : Object
{ payload: 1735739813554, topic: "", _msgid: "1fd5655c6246ca2a" }
01/01/2025, 13:56:58node: debug 4msg : Object
{ payload: 1735739817555, topic: "", _msgid: "c519cb2b67f566e5" }
01/01/2025, 13:57:02node: debug 4msg : Object
{ payload: 1735739821556, topic: "", _msgid: "583773cb0ad4f7f8" }
01/01/2025, 13:57:06node: debug 4msg : Object
{ payload: 1735739825558, topic: "", _msgid: "bb272e046efdd22f" }

[
    {
        "id": "4d610463d3d06de6",
        "type": "inject",
        "z": "268bb517b3ae6cbc",
        "d": true,
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "1",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 550,
        "y": 160,
        "wires": [
            [
                "ed0a0b9aa17b9353"
            ]
        ]
    },
    {
        "id": "ed0a0b9aa17b9353",
        "type": "delay",
        "z": "268bb517b3ae6cbc",
        "name": "",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "2",
        "nbRateUnits": "8",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": true,
        "allowrate": false,
        "outputs": 1,
        "x": 720,
        "y": 160,
        "wires": [
            [
                "8f8cdd9bac748222"
            ]
        ]
    },
    {
        "id": "8f8cdd9bac748222",
        "type": "debug",
        "z": "268bb517b3ae6cbc",
        "name": "debug 4",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 920,
        "y": 160,
        "wires": []
    }
]

You could use a batch node along with a switch node to do this.

[{"id":"da45394df2146f4a","type":"inject","z":"d1395164b4eec73e","name":"","props":[{"p":"reset","v":"true","vt":"bool"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":210,"y":6840,"wires":[["c5f248a56bbebba9"]]},{"id":"c5f248a56bbebba9","type":"batch","z":"d1395164b4eec73e","name":"","mode":"count","count":"8","overlap":0,"interval":"8","allowEmptySequence":false,"honourParts":false,"topics":[],"x":420,"y":6840,"wires":[["541b0470c1d7ea59"]]},{"id":"bc161b878398c215","type":"change","z":"d1395164b4eec73e","name":"simulate incoming messages","rules":[{"t":"delete","p":"parts","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":7000,"wires":[["c5f248a56bbebba9"]]},{"id":"541b0470c1d7ea59","type":"switch","z":"d1395164b4eec73e","name":"","property":"parts.index","propertyType":"msg","rules":[{"t":"lt","v":"2","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":550,"y":6840,"wires":[["d9681d534d24c87a"]]},{"id":"0966fbeb683a9328","type":"split","z":"d1395164b4eec73e","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":290,"y":7000,"wires":[["bc161b878398c215"]]},{"id":"d9681d534d24c87a","type":"delay","z":"d1395164b4eec73e","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":700,"y":6840,"wires":[["8f8cdd9bac748222"]]},{"id":"4d610463d3d06de6","type":"inject","z":"d1395164b4eec73e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]","payloadType":"json","x":170,"y":7000,"wires":[["0966fbeb683a9328"]]},{"id":"8f8cdd9bac748222","type":"debug","z":"d1395164b4eec73e","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":840,"y":6900,"wires":[]}]

[edit] Or a simple function to limit to the first 2 of any 8 messages may do what you require

let count = (context.get("count") ?? 0) + 1;
if (count > 8){
  count  = 1;
}
if (count > 2){
  msg = null;
}
context.set("count", count);
return msg;

Thanks, I'll have a look at that but I still think this node doesn't really work as expected. As soon as you enter a numeral greater than 1 in the first box it should behave as I explained.

This has been discussed a few times over the years, and while, logically, you may well be correct, such a change in behaviour would be a breaking change (it could cause existing flows to break), which reduces the chance of it being implemented.

Is this relevant?
https://discourse.nodered.org/t/limit-message-number-per-hour/53724

It may do but if it's incorrect it shouldn't stay incorrect it should be corrected!

I would have thought that few would have used the node as anything other than 1 message per ... unless they had wanted it to behave as I would expect it to behave. After all, who would put 2 messages per 8 seconds if they actually wanted 1 message every 4 seconds!

Or an additional node should be created and the other depreciated.

It isn't a bug, it performs according to the documented spec. Whether it is 'incorrect' is a matter of interpretation.

Yes, thank you.

It shouldn't be that complicated though and the logic in the node is wrong. Why does it even have a box to fill in of 'x' messages when it's pointless because of the way the node works at present.?

Reminds me of ISO9000. As long as you conform to the way you say you are going to do it then it's correct even though what you are conforming to is wrong.

As I said, whether the behaviour is wrong is a matter of interpretation. The current behaviour is exactly as documented in the help text.
"When configured to rate limit messages, their delivery is spread across the configured time period".

You might want to say 100 messages/hour, so the box to fill in the number of messages is needed.

The meaning of rate limit is not spread over a given time period it is within a time period.The node contains the word 'per'

You would put 1 per 0.6 minutes or 1 per 36 seconds.

While I sympathise with your irritaion at the node, I don't agree.
If "2 messages per 8 seconds" means two messages are passed on a millisecond apart (because they are the first two to arrive, a millisecond apart) that's clearly a rate of 1000 messages per second followed by a period with a rate of 0 messages per second.

An important use of the rate limit node is to protect processes further down the flow from being overwhelmed.
If messages are not equally spaced it can't do this.

Incidentally, if you set the delay rate to 1 message per 1.25 seconds the default label for the node is correct.
image
But if you set it to 1 message per 0.25 seconds the default label becomes
image

If "2 messages per 8 seconds" means two messages are passed on a millisecond apart (because they are the first two to arrive, a millisecond apart) that's clearly a rate of 1000 messages per second followed by a period with a rate of 0 messages per second.

Yes that's a 'rate of ' not a 'rate limit'. Your example needs 2 rate limits because thats what you have.

https://www.cloudflare.com/learning/bots/what-is-rate-limiting/

'Rate limiting is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a certain time frame'

google AI
'Rate limiting is a technique that controls the number of requests sent or received by a network interface controller or server within a given time period'.

There are four main types of rate limiting
Fixed window
Sliding window
Token bucket
Leaky bucket.

The delay node seems to use the leaky bucket algorithm. As others have said it is unlikely for the maintainer to change this as it would break other flows . It may possibly to add the different algorithms as options, this may take a lot of work ( most here are volunteers. PR's are always welcome). The simpler option IMHO is for you to write your own fixed window function.

1 Like

Dont think its leaky bucket.

Leaky Bucket Rate Limiting

Leaky bucket rate limiting algorithms differ from fixed-window algorithms because they don’t rely on specified timeframes. They focus on the fixed length of request queues without considering the time. The server will service requests on a first-come, first-serve basis. New requests join the back of the queue. The server will drop a new request if it arrives when the queue is full.

Leaky Bucket is represented by a bucket with a leak, emptying its contents at a constant rate. Incoming requests are added to the bucket, functioning as a queue. If the bucket overflows, new requests are either delayed or discarded, as illustrated.

Figure 6. Leaky Bucket Algorithm

This algorithm is typically used to smooth out traffic patterns, prevent bursts in data transmission, and control the rate of data processing.

We will have to agree to disagree on this. Rate Limiting: Key Concepts and Use Cases

2 Likes

I've put a subflow in to get round it.