Skip to content

Instantly share code, notes, and snippets.

@shanness
Last active March 3, 2021 22:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shanness/80390da72f235b760f853a4bda79668b to your computer and use it in GitHub Desktop.
Save shanness/80390da72f235b760f853a4bda79668b to your computer and use it in GitHub Desktop.
Time based Ratelimit / Throttle per topic

Defaults to 10 minutes, override with msg.ratelimitms (or edit the node)

Defaults to requiring 2 hits within the defined time frame to pass on, override with msg.countToTrigger (or edit node)

Groups by msg.topic (one counter per topic).

Discards messages (per topic) until timeout reached.

Designed for reusable flows, such as a notification system that should only notify once per problem type (topic) each 10 minutes.

[{"id":"5b24df49.412f6","type":"function","z":"136ed48.14e8c2c","name":"Rate Limiter","func":"// Defaults to 10 minutes, overridable with msg.ratelimitms\n// Will limit per topic (including by null ones)\nvar interval = msg.ratelimitms || (1000*60*10); // minimum interval between messages (ms)\nvar countToTrigger = msg.countToTrigger || 2;\nvar lastReleasedKey = \"lastReleased_\" + (msg.topic.replace(/ /g, \"_\") || \"null_topic\");\nvar lastHitKey = \"lastHit_\" + (msg.topic.replace(/ /g, \"_\") || \"null_topic\");\nvar cntKey = \"count_\" + (msg.topic.replace(/ /g, \"_\") || \"null_topic\");\nvar lastReleased = (context.get(lastReleasedKey) || 0);\nvar lastHit = (context.get(lastHitKey) || 0);\nvar now = Date.now();\nif (now-lastHit > interval) {\n node.warn(\"resetting count, too far apart.\");\n context.set(cntKey,0); // Clear hits, too far apart.\n}\ncontext.set(lastHitKey,now);\nif (now-lastReleased > interval) {\n var count = (context.get(cntKey) || 0);\n count++;\n if (count >= countToTrigger) {\n context.set(lastReleasedKey,now);\n context.set(cntKey,0);\n node.status({fill:\"green\",shape:\"ring\",text:\"passed on\"});\n return msg;\n } else {\n node.status({fill:\"red\",shape:\"ring\",text:\"Waiting for \" + \n (countToTrigger - count) + \" more hits.\"});\n context.set(cntKey,count);\n return null;\n }\n} else {\n var timeLeft = (interval - (now-lastReleased)) / 1000; \n node.status({fill:\"red\",shape:\"ring\",text:\"blocked for \" + timeLeft + \" seconds\"});\n return null;\n}","outputs":1,"noerr":0,"x":845.0173416137695,"y":274.01039695739746,"wires":[["218e2c7f.b39ab4","a759593e.6deeb8"]]}]
@meeki007
Copy link

hey again. you have no copyright information.
Want to use your code to make a option for by topic on timeframerlt
I'm the author of timeframerlt and I made it to ignore topic on purpose. anything comes in and it up's the count.
now i see your needs go beyond that.

I want to make a dropdown that says "By Topic" and second choice "Ignore Topic"
Also going to add a field to change the msg.paylod to user choice msg.*

If not I'll leave you alone and not add or change anything to my current timeframerlt node.

@shanness
Copy link
Author

Hey @meeki007,

Sorry for the very long delay getting back to you. You are very welcome to use this code however you'd like, and would be great to integrate this in timeframeit :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment