Last active
October 20, 2018 21:52
-
-
Save alvpickmans/215b437b8fbe5170f9dd34974518f2a9 to your computer and use it in GitHub Desktop.
Handling multiple MongoDB connections on Node.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
/** | |
* Module to create a Clients Pool | |
* | |
*/ | |
/** | |
* Dependencies | |
*/ | |
let MongoClient = require('mongodb').MongoClient; | |
let crypto = require ('crypto'); | |
/** | |
* Clients Pool | |
* @param {number} capacity Pool capacity. Default = 100 | |
* @param {number} timeOutMS Time a connection is kept active in miliseconds. Default = 1000 * 60 | |
*/ | |
let ClientsPool = function (capacity, timeOutMS) { | |
this.capacity = typeof (capacity) == 'number' ? capacity : 100; | |
this.timeOutMS = typeof (timeOutMS) == 'number' ? timeOutMS : 1000 * 60; | |
this.clients = {}; | |
this.size = 0; | |
this.start(); | |
} | |
/** | |
* Method that creates a new connection and adds it to the pool if not existing, | |
* or updates its expiring date if it does. | |
* @param {string} mongoUri URI for the MongoDB instance | |
* @param {string} username Username | |
* @param {string} password Password | |
* @param {Object} connectionOptions MongoClient connection options | |
* @returns MongoClient | |
* @throws MongerError if the pool has reached its maximum capacit | |
*/ | |
ClientsPool.prototype.connect = async function (mongoUri, username, password, connectionOptions = {}) { | |
// If no credentials, this is worthless. | |
if(!username || !password){ throw Error("Username and/or password not provided."); } | |
// Key being used to access the active clients | |
let key = crypto.createHash('sha1').update(`${username}:${password}`).digest('base64'); | |
let clientInstance = this.clients[key]; | |
let currentDate = Date.now(); | |
// If the client doesn't exists, try to create a new instance | |
if(!clientInstance){ | |
// If has reached the maximum, gently reject the request | |
if(this.size >= this.capacity){ | |
throw new Error("Server cannot handle more request. Please try again in a few minutes."); | |
} | |
// Increase the size of the pool | |
this.size++; | |
clientInstance = { | |
expireDate: -1, | |
client: null | |
} | |
} | |
// If is new or has expired for some reason, create a new connection | |
if( clientInstance.expireDate <= currentDate || clientInstance.client == null || !clientInstance.client.isConnected() ){ | |
connectionOptions.auth = { | |
"user": username, | |
"password": password | |
}; | |
clientInstance ={ | |
client: await MongoClient.connect(mongoUri, connectionOptions), | |
expireDate: currentDate + this.timeOutMS | |
} | |
// If not expired yet, update its expireDate | |
}else{ | |
clientInstance.expireDate = currentDate + this.timeOutMS; | |
} | |
this.clients[key] = clientInstance; | |
return clientInstance.client; | |
} | |
/** | |
* Method that checks the current open connections and | |
* closes any that has already expired | |
*/ | |
ClientsPool.prototype.checkClients = function(){ | |
let currentDate = Date.now(); | |
for(var key in this.clients){ | |
let clientInstance = this.clients[key]; | |
if(clientInstance && clientInstance.expireDate <= currentDate){ | |
clientInstance.client.close(); | |
delete this.clients[key]; | |
this.size--; | |
} | |
} | |
} | |
/** | |
* Method that runs the @see{@link checkClients} method at intervals | |
* equal half of the connection timeout time. | |
*/ | |
ClientsPool.prototype.loop = function(){ | |
setInterval(function(){ | |
this.checkClients(); | |
}.bind(this), this.timeOutMS/2); | |
}; | |
/** | |
* Method to start/initiate the pool and its background process. | |
*/ | |
ClientsPool.prototype.start = function(){ | |
this.checkClients(); | |
this.loop(); | |
} | |
/** | |
* Exporting the module | |
*/ | |
module.exports = ClientsPool; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment