Skip to content

Commit

Permalink
Refactor mongo-storage.js (nightscout#6589)
Browse files Browse the repository at this point in the history
* Refactor `mongo-storage.js`

* Use async/await instead of promises
* Fixes a bug in the previous implementation where a promise error was
  not being caught and handled

* Code cleanup
  • Loading branch information
pazaan authored and tanja3981 committed Jan 6, 2021
1 parent e8d5612 commit 1eaba09
Showing 1 changed file with 60 additions and 66 deletions.
126 changes: 60 additions & 66 deletions lib/storage/mongo-storage.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
'use strict';

var mongodb = require('mongodb');
var connection = null;
var MongoClient = mongodb.MongoClient;
var mongo = {};
const MongoClient = require('mongodb').MongoClient;

function init (env, cb, forceNewConnection) {
const mongo = {
client: null,
db: null,
};

function maybe_connect (cb) {
function init(env, cb, forceNewConnection) {

if (connection != null && !forceNewConnection) {
function maybe_connect(cb) {

if (mongo.db != null && !forceNewConnection) {
console.log('Reusing MongoDB connection handler');
// If there is a valid callback, then return the Mongo-object
mongo.db = connection;

if (cb && cb.call) {
cb(null, mongo);
Expand All @@ -23,76 +24,69 @@ function init (env, cb, forceNewConnection) {
}

console.log('Setting up new connection to MongoDB');
var timeout = 30 * 1000;
var options = {
reconnectInterval: 10000
, reconnectTries: 500
, connectTimeoutMS: timeout
, socketTimeoutMS: timeout
, useNewUrlParser: true
const timeout = 10 * 1000;
const options = {
connectTimeoutMS: timeout,
socketTimeoutMS: timeout,
useNewUrlParser: true,
useUnifiedTopology: true,
};

var connect_with_retry = function(i) {

MongoClient.connect(env.storageURI, options)
.then(client => {
console.log('Successfully established a connected to MongoDB');

var dbName = env.storageURI.split('/').pop().split('?');
dbName = dbName[0]; // drop Connection Options
mongo.db = client.db(dbName);
connection = mongo.db;
mongo.client = client;

mongo.db.command({ connectionStatus: 1 }).then(
result => {
const roles = result.authInfo.authenticatedUserRoles;
if (roles.length > 0 && roles[0].role == 'readAnyDatabase') {
console.error('Mongo user is read only');
cb(new Error('MongoDB connection is in read only mode! Go back to MongoDB configuration and check your database user has read and write access.'), null);
}

console.log('Mongo user role seems ok:', roles);

// If there is a valid callback, then invoke the function to perform the callback
if (cb && cb.call) {
cb(null, mongo);
}
}
);
})
.catch(err => {
if (err.message && err.message.includes('AuthenticationFailed')) {
console.log('Authentication to Mongo failed');
cb(new Error('MongoDB authentication failed! Double check the URL has the right username and password in MONGODB_URI.'), null);
return;
}

if (err.name && err.name === "MongoNetworkError") {
var timeout = (i > 15) ? 60000 : i * 3000;
console.log('Error connecting to MongoDB: %j - retrying in ' + timeout / 1000 + ' sec', err);
setTimeout(connect_with_retry, timeout, i + 1);
if (i == 1) cb(new Error('MongoDB connection failed! Double check the MONGODB_URI setting in Heroku.'), null);
} else {
cb(new Error('MONGODB_URI ' + env.storageURI + ' seems invalid: ' + err.message));
}
});

const connect_with_retry = async function (i) {

mongo.client = new MongoClient(env.storageURI, options);
try {
await mongo.client.connect();

console.log('Successfully established a connected to MongoDB');

const dbName = mongo.client.s.options.dbName;
mongo.db = mongo.client.db(dbName);

const result = await mongo.db.command({ connectionStatus: 1 });
const roles = result.authInfo.authenticatedUserRoles;
if (roles.length > 0 && roles[0].role == 'readAnyDatabase') {
console.error('Mongo user is read only');
cb(new Error('MongoDB connection is in read only mode! Go back to MongoDB configuration and check your database user has read and write access.'), null);
}

console.log('Mongo user role seems ok:', roles);

// If there is a valid callback, then invoke the function to perform the callback
if (cb && cb.call) {
cb(null, mongo);
}
} catch (err) {
if (err.message && err.message.includes('AuthenticationFailed')) {
console.log('Authentication to Mongo failed');
cb(new Error('MongoDB authentication failed! Double check the URL has the right username and password in MONGODB_URI.'), null);
return;
}

if (err.name && err.name === "MongoServerSelectionError") {
const timeout = (i > 15) ? 60000 : i * 3000;
console.log('Error connecting to MongoDB: %j - retrying in ' + timeout / 1000 + ' sec', err);
setTimeout(connect_with_retry, timeout, i + 1);
if (i == 1) cb(new Error('MongoDB connection failed! Double check the MONGODB_URI setting in Heroku.'), null);
} else {
cb(new Error('MONGODB_URI ' + env.storageURI + ' seems invalid: ' + err.message));
}
}
};

return connect_with_retry(1);

}
}

mongo.collection = function get_collection (name) {
return connection.collection(name);
mongo.collection = function get_collection(name) {
return mongo.db.collection(name);
};

mongo.ensureIndexes = function ensureIndexes (collection, fields) {
fields.forEach(function(field) {
mongo.ensureIndexes = function ensureIndexes(collection, fields) {
fields.forEach(function (field) {
console.info('ensuring index for: ' + field);
collection.createIndex(field, { 'background': true }, function(err) {
collection.createIndex(field, { 'background': true }, function (err) {
if (err) {
console.error('unable to ensureIndex for: ' + field + ' - ' + err);
}
Expand Down

0 comments on commit 1eaba09

Please sign in to comment.