jProg2015
jProg2015

Reputation: 1128

Preventing MongoDB Multiple Connections in NodeJS

I've seen a few posts on this topic; one of which has helped me progress - but I still have a problem.

My Mongo database is being flooded with connections and throwing errors. I do have multiple different connections which I am establishing using customer name. Online I found this useful class:

export default class ConnectionManager {
static databases: any = {};

static getConnection(customer: string) : Promise<typeof mongoose> {
    if (this.databases[customer]) return Promise.resolve(this.databases[customer]);

    return new Promise<typeof mongoose>((resolve: any, reject: any) => {
        mongoose.connect(process.env.MONGOOSE_BASE_SERVER_URL + customer, { useNewUrlParser: true })
            .then((newDb: mongoose.Mongoose) => { 
                this.databases[customer] = newDb;
                resolve(this.databases[customer]);
            });
    });
  }
}

As I've mentioned, this helps - it works if a connection already exists.

The trouble is I have a scheduled task which could immediately flood the API with requests after existing connections have timed out. If the API is flooded with requests at this point - they come in too quickly and it opens up hundreds of connections and I still encounter the same problem; before ConnectionManager.databases has had chance to be updated with a new connection.

Racking my brains to think of a way of avoiding this. Does anyone have any advice?

Thanks,

EDIT - Think this is mainly occurring after a service restart. Here is an example error:

{ Error: read ECONNRESET
    at TCP.onread (net.js:660:25)
  name: 'MongoNetworkError',
  errorLabels: [ 'TransientTransactionError' ],
  [Symbol(mongoErrorContextSymbol)]: {} }
{ MongoNetworkError: connection 202 to localhost:27017 closed
    at Socket.<anonymous> (.../node_modules/mongodb-core/lib/connection/connection.js:275:9)
    at Object.onceWrapper (events.js:273:13)
    at Socket.emit (events.js:182:13)
    at TCP._handle.close (net.js:599:12)
  name: 'MongoNetworkError',
  errorLabels: [ 'TransientTransactionError' ],
  [Symbol(mongoErrorContextSymbol)]: {} }

Upvotes: 1

Views: 1011

Answers (1)

Estus Flask
Estus Flask

Reputation: 222309

Promises naturally provide caching behaviour because resolved promise provides same result when it is chained.

A proper approach would be to store promises in databases, this way there won't be race conditions that result in multiple connections of the same name, i.e. databases[customer] = Promise.resolve(mongoose.connect(...)).

But this isn't needed for Mongoose which internally chains connection promises; connection object can be just saved and used. Multiple connections are created with createConnection method.

Also, static-only classes are antipatterns. It can be an object or just a function:

export const _databases = {};

export const getConnection = (customer) => {
  if (!_databases[customer])
    _databases[customer] = mongoose.createConnection(
      process.env.MONGOOSE_BASE_SERVER_URL + customer,
      { useNewUrlParser: true }
    );

  return _databases[customer];
}

There is no obvious reason for databases object to be public export. It can be exported for testing purposes.

Upvotes: 2

Related Questions