Node Developers: How To use setMaxListeners() using RED.events

Hi folks,
while working on my nodes, I also saw the "MaxListenersExceededWarning: Possible EventEmitter memory leak detected..." error. After some research on how to finally solve that, I'd like to share that with you, as I see a lot of questions regarding this warning here in the forum, but did not see an example how to solve it.

The problem with this message is, that it is a warning to prevent the system to loose performance because of too many listeners. If an event is emitted, it is used and processed by all registered listeners. That take processing time. The actual problem here is, if a node continues to add listeners all the time without removing them once they are not need anymore, the system gets slower and slower. We don't want that, of course. So this warning shows the developers (and the users), that there might be a problem. So if we remove the warning, we should be sure, that we don't have this kind of leaking.

This must be done by node developers. Users cannot do that, unless the node has an option to set this value in the editor or in a config node.

There are 2 situation where this error can appear:

  1. In nodes like mqtt or websocket
    They directly use Sockets as part of their code. If you take a look at the code, you see, that they setMaximumListeners in the socket client.
  2. In nodes, that use RED.events.register
    Here we use an internal event system, that NR provides in RED.events. The default limit here is set to 11.

In the first situation you can see, that the socket clients are explicitly set, so you should never see the warning here.

More common and interesting for developers, that use eventing in their nodes based on RED.events, the solution is pretty easy. It looks like that:

// in "mynode.js"
module.exports = function (RED) {
   /*
   set to 0? - be sure you really don't want a warning!
   Otherwise set this to the expected limit.
   */
   MAX_LISTENERS = 0
   RED.events.setMaxListeners(MAX_LISTENERS)
   ... // continue setting up your nodes

That's it.

As usual, be warned to do that again. By switching the maximum numbers of listeners to 0 (or a very high number), you might run into trouble.

I hope that brings some light into that matter for some of you.

You should not be modifying the internal configuration of RED.events. By changing that max, you will then prevent any real listener leaks from being reported.

If you are using that for the internal operation of your nodes, you should create your own EventEmitter object and share that between your nodes. You are then free to modify the limit as you see fit.

https://nodejs.org/api/events.html#events_events

well yes, that will switch off the warning for all nodes using RED.events.

Is RED.events not supposed to be used by custom nodes after all?
Because that kinds of limits the use cases for RED.events in nodes in the first place, as it expects to use only 11 listeners per event. Wouldn't it be nice to have a ready-to-use event system for nodes, that allows to configure that per node or even per event?

So the preferred way to go is to implement a custom Emitter for each node?

RED.events is the internal event emitter for the core runtime. It wasn't intended to be a general purpose event bus for nodes to use internally - it's there so nodes have access to lifecycle events of the runtime if they need them.

In general, nodes should be communicating via the messages that pass over the wires between them. There will be very specific exceptions to that - such as your component nodes, but on the whole, nodes don't need it.

Given it's an exceptional pattern, then I'm not sure there is a place to have a general event bus provided by the runtime that bypasses the proper way for nodes to communicate.

Yes - Create an eventEmitter inside your module and share an internal reference to it between your nodes. That also stops other nodes from snooping in on your internal events.

Thanks for the insight.

So just for the sake of completeness, here is my example of how to use a custom Emitter:

const EventEmitter = require('events');

module.exports = function (RED) {
   /*
   set to 0? - be sure you really don't want a warning!
   Otherwise set this to the expected limit.
   */
   MAX_LISTENERS = 0

   class CustomEmitter extends EventEmitter {}
   const emitter = new CustomEmitter()
   emitter.setMaxListeners(MAX_LISTENERS) // remove "MaxListenersExceededWarning"

   // continue setting up your nodes
   // replace any occurance of "RED.events" in your code with "emitter"
   // RED.events.addListener("my-event", handlerMethod)
   emitter.addListener("my-event", handlerMethod)
   ...
   emitter.emit("my-event", msg) // send the message

Make sure you remove any active listeners in the close processing for the node. That may be why you got the warning in the first place. I don't believe you should need to mess is that setting at all.

the warning appears, as soon as you have more than 10 listeners. So if you use 11+ nodes (instances) of the same type in your flows, listening to an event, it will show up, annoying the users.

And of course I do remove them again on close :slight_smile:

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.