Reputation: 1128
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
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