Skip to content

Instantly share code, notes, and snippets.

@alvpickmans
Last active October 20, 2018 21:52
Show Gist options
  • Save alvpickmans/215b437b8fbe5170f9dd34974518f2a9 to your computer and use it in GitHub Desktop.
Save alvpickmans/215b437b8fbe5170f9dd34974518f2a9 to your computer and use it in GitHub Desktop.
Handling multiple MongoDB connections on Node.js
'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