15

We are using GraphQL Subscriptions and pubsub to subscribe to posts.

When more than 10 subscriptions occur we get the the node warning "MaxListenersExceededWarning: Possible EventEmitter memory leak detected."

Is it possible to raise the max listeners in the pubsub class?

The pubsub class is inside a separate module and looks like this:

import { PubSub } from 'graphql-subscriptions';

const pubsub = new PubSub();

export { pubsub };

The subscription server looks like this:

import { SubscriptionManager } from 'graphql-subscriptions';
import { createServer } from 'http';
import { SubscriptionServer } from 'subscriptions-transport-ws';

import { pubsub } from './subscriptions';
import executableSchema from './executableSchema';

const WS_PORT = 8080;

const websocketServer = createServer((request, response) => {
  response.writeHead(404);
  response.end();
});

websocketServer.listen(WS_PORT, () => console.log(
  `Websocket Server is now running on http://localhost:${WS_PORT}`
));

const subscriptionManager = new SubscriptionManager({
  schema: executableSchema,
  pubsub: pubsub,
  setupFunctions: {
        newPost: (options, args) => {
         return {
            newPostChannel: {
               filter: (post) => {
                  return args.publicationId === post.relatedPublication.id;
               }
            },
         };
      },
  },
});

const subscriptionServer = new SubscriptionServer({
  subscriptionManager: subscriptionManager
}, {
  server: websocketServer,
  path: '/',
});


export {
  subscriptionServer,
};
Locco0_0
  • 3,420
  • 5
  • 30
  • 42

3 Answers3

17

I wrote the original implementation of the graphql-subscriptions package you're using, so I can provide some context here.

The simple EventEmitter pubsub library included in graphql-subscriptions is only intended for demo purposes. EventEmitters don't really scale to large numbers, they're in-memory, and they'll only work as long as you have no more than a single server.

For anyone trying to run GraphQL subscriptions in production, I strongly recommend using a different system, for example Redis or MQTT through graphql-redis-subscriptions or graphql-mqtt-subscriptions. This will have the advantage of keeping the GraphQL server stateless (apart from the websockets) and thus easy to scale horizontally.

helfer
  • 7,042
  • 1
  • 20
  • 20
  • 3
    "...I strongly recommend using a different system..." thanks, I appreciate the info. – Alexander Sep 01 '17 at 22:16
  • 5
    This should really be added to the graphql-subscriptions readme. I did not realise this and have been running the default EventEmitter setup in production for a couple of months now! (and have consistently been getting memory leak warnings) – mxstbr Oct 05 '17 at 07:33
  • I went ahead and submitted a PR to add a note about this to the readme of the package: https://github.com/apollographql/graphql-subscriptions/pull/110 – mxstbr Oct 05 '17 at 07:40
  • 1
    A single node.js instance can handle up to about 65K websocket connections. So if mine is a small service that only makes few K connections, just using `graphql-subscription` with `setMaxListeners` increased should be fine, right? – Sihoon Kim Jan 26 '21 at 07:07
5

ee is a protected member of PubSub, so setting it directly will cause errors in a TypeScript project. However, you can pass an EventEmitter with an adjusted MaxListener count to the PubSub constructor:

import { PubSub } from 'apollo-server-express';
import { EventEmitter } from 'events';

const biggerEventEmitter = new EventEmitter();
biggerEventEmitter.setMaxListeners(30);
const pubSub = new PubSub({eventEmitter: biggerEventEmitter});
Keith Gillette
  • 141
  • 3
  • 11
4

Found out that you can change the max listeners in the event emitter of the pubsub instance, like so:

import { PubSub } from 'graphql-subscriptions';

const pubsub = new PubSub();
pubsub.ee.setMaxListeners(30); // raise max listeners in event emitter

export { pubsub };
Locco0_0
  • 3,420
  • 5
  • 30
  • 42