Node.js: module system, object references - node.js

// connections.js
...
module.exports = {
conn: {
mongodb: {
connect: function() {throw ...},
disconnect: function() {throw ...},
getState: function() {throw...}
},
mysql: {
connect: function() {throw ...},
disconnect: function() {throw ...},
getState: function() {throw ...}
}
},
drivers: {
mongoose: mongoose,
mysql: mysql
},
states: connectionStates,
setup: function(config, cb) {
// provides concrete implementations of connect(), discconnect(),
// getState(), sets up listeners to relay connection events
this.conn.mongodb = setupMongo(config.mongodb);
this.conn.mysql = setupSql(config.mysql);
...
cb();
}
};
Now if I include this as:
// main.js
var connections = require(__dirname + '/connections'),
conn = connections.conn,
db = conn.mongodb;
// connectionections.setup() not been called yet
exports.foo = function() {
// connections.setup() already been called before this point
db.connect(...); // fails - error thrown - using the abstract function
conn.mongodb.connect(...); // works
}
Why does the first one fail? The db var should contain a reference to connections.conn.mongodb? At very least, I'd expect both to either work, or not work. What is the difference that allows the first to fail and second to succeed? Thank you

Its failing in the first case because setup() was called in a different scope and db/conn.mongodb diverged (with a copy on write) when setup was called. If you compare db and conn.mongodb in the exports.foo function, you should see that conn.mongodb has been initialized with the setupMongo and db still has the uninitialized versions. Not sure what the code looks like that is calling connections.setup, but from the looks of this, db !=== conn.mongodb.

Related

How to get MongoDB database stats with mongoose?

I'm trying to get MongoDB stats such as total database size for health metrics using mongoose, but haven't been able to do so.
I found this piece of code. but it doesn't seem to work.
db.js:
const db = mongoose.connect(config.MONGODB_URL, settings, (err) => {
mongoose.set('useFindAndModify', false);
logger.info(`connecting ${config.MONGODB_URL}`);
if (err) {
logger.error(`failed db connection: ${err}`);
}
});
module.exports = db;
monitor.js:
const db = require('../store/db');
function mongoUsage(cb) {
db.db.stats((err, data) => {
logger.debug(data);
});
cb();
}
However I'm getting TypeError: Cannot read property 'stats' of undefined error when running the function.
I think, you have to make sure at first that your database connection has been established. In the provided example, you may notice db.once call which waits for open event to occur. In other words, we have a pending connection to the database running and the goal is to get notified if we connect successfully. Therefore, your connection object is still in the process of its creation, and thus undefined yet.
Try to modify your files accordingly assuming you have defined logger, config and settings appropriately:
db.js:
const mongoose = require('mongoose');
mongoose.connect(config.MONGODB_URL, settings, (err) => {
mongoose.set('useFindAndModify', false);
logger.info(`connecting ${config.MONGODB_URL}`);
if (err) {
logger.error(`failed db connection: ${err}`);
}
});
module.exports = mongoose.connection;
monitor.js:
const db = require('../store/db');
db.once('open', () => {
// call stats directly
db.db.stats((err, data) => {
logger.debug(data);
});
// or you can call your mongoUsage here
});
With the code above I succesfully get the following result:
{ db: 'test-db',
collections: 1,
views: 0,
objects: 1,
avgObjSize: 844,
dataSize: 844,
storageSize: 36864,
numExtents: 0,
indexes: 6,
indexSize: 221184,
fsUsedSize: 2271096832,
fsTotalSize: 19163181056,
ok: 1 }
I hope it helps to resolve your issue.

Initialization of db connection - nodejs

I want to use gridfs-stream in a nodejs application.
A simple example is given in the documentation:
var mongoose = require('mongoose');
var Grid = require('gridfs-stream');
Grid.mongo = mongoose.mongo;
mongoose.connect('mongodb://localhost:27017/test');
// make sure the db instance is open before passing into `Grid`
mongoose.connection.once('open', function () {
var gfs = Grid(mongoose.connection);
// all set!
})
My problem is described by the comment:
make sure the db instance is open before passing into Grid
I try to use gfs in a post request. Now when the code gets initialized, the gfs variable is not defined yet.
api.post('/upload', function(req, res) {
req.pipe(gfs.createWriteStream({
filename: 'test'
}).on('close', function(savedFile){
console.log('file saved', savedFile);
return res.json({file: savedFile});
}));
})
Initializing my route from a callback seems kind of odd.
I read in this post (Asynchronous initialization of Node.js module) that require('') is performed synchronous, and since I rely on the connection being established, I'm kind of forced to wait
Basically I'm not sure if I should use a async pattern on startup now, or if I just miss a more elegant way to solve this.
I have a very similar problem with my server. In my case I am reading https certs asynchronously, the software version from git asynchronously and I want to make sure I have it all together by the time the user comes to log in so I can pass the software version back as a reply to login.
The solution is to use promises. Create the promises on user start up for each activity. Then in the code where you want to be sure its all ready, just call then on either the promise itself or Promise.all(array of promises).then()
Here is an example of what I am doing to read the ssl certs to start the server
class Web {
constructor(manager,logger) {
var self = this;
this.server = false;
this.logger = logger;
var key = new Promise((resolve,reject) => {
fs.readFile(path.resolve(__dirname, 'key.pem'),(err,data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
var cert = new Promise((resolve,reject) => {
fs.readFile(path.resolve(__dirname, 'certificate.pem'), (err,data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
Promise.all([key,cert]).then(values => {
var certs = {
key: values[0],
cert: values[1],
};
return certs;
}).then(certs => {
self.server = require('http2').createServer(certs,(req,res) => {
// NOW Started and can do the rest of the stuff
});
self.server.listen(...);
});
NEEDS SOME MORE CLOSING BRACKETS

call db.loadServerScripts on connection startup

I have some server-side helper functions in system.js collection, which are then used in node.js. However sometimes, they are undefined.
Here is the scenario:
I load these functions once, on server start:
db.eval('db.loadServerScripts()', function(err, result) { ... });
So this is called only once on start, not for every request.
From now on I can call e.g.:
db.eval('getNextSequence(\'test\')', function(err, seq){});
But sometimes I get the error, that getNextSequence is undefined. I suspect those functions to exist only in current connection scope. So maybe when node receives new connection, the functions are not set.
Is there any way to use those functions in node.js, but to have them reliably available always?
Example scenario:
//1./ this function is stored in system.js
getNextSequence: function(name)
{
var ret = db.counters.findAndModify({
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true,
upsert: true
});
return ret ? ret.seq : null;
}
//2./ this is called on nodejs server startup (once for server lifetime)
var mongo = require('mongoskin');
var db = mongo.db("mongodb://localhost:27017/mydb", {native_parser:true});
//...
db.eval('db.loadServerScripts()', function(err, result) {
//...crash if failed
});
//3./ this is used in node.js code, on request processing:
db.eval('getNextSequence(\'someNamespace\')', function(err, seq){
// int seq is converted to string slug
// a new entity with slugId is saved to collection
});

DB Connection in mongo-db native client

I have an express/nodeJs app which will use mongo-db native client for persistance in Mongo-db. Now my problem is that most of the example I have seen have one collection and therefore do the connection in that js file, like this tutorial(which is mentioned in mongo-db native client docs). There the connection code is like this:
var Db = require('mongodb').Db;
var Connection = require('mongodb').Connection;
var Server = require('mongodb').Server;
var BSON = require('mongodb').BSON;
var ObjectID = require('mongodb').ObjectID;
ArticleProvider = function(host, port) {
this.db= new Db('node-mongo-blog', new Server(host, port, {auto_reconnect: true}, {}));
this.db.open(function(){});
};
ArticleProvider.prototype.getCollection= function(callback) {
this.db.collection('articles', function(error, article_collection) {
if( error ) callback(error);
else callback(null, article_collection);
});
};
ArticleProvider.prototype.findAll = function(callback) {
this.getCollection(function(error, article_collection) {
if( error ) callback(error)
else {
article_collection.find().toArray(function(error, results) {
if( error ) callback(error)
else callback(null, results)
});
}
});
};
There are other methods also which I kept out to keep it small(check in the above url for full tutorial).
My problem is that I have few more collections and therefore I am worried as to how to make a single connection to the database and use it for all the collections. I would also like if you can specify how to make connections to replica-sets also for reads and the main database for writes.
Or should I make calls to connections in each of my collection.js files like the above mentioned tutorial has done in one.
Please help me.
Try using the singleton pattern:
In your main express app.js connect to the database and create the database instance:
var Database = require('database.js');
...
mongodb.connect(config.dbAddress, function (err, db) {
if(err) {
return console.log('Error connection to DB');
}
Database.setDB(db);
app.listen(config.appPort);
});
Then in any other file you need to use the database require database.js again:
var Database = require('database.js');
...
ArticleProvider = function() {
this.db = Database.getDB();
};
...
Finally the database.js file follows the singleton pattern:
/**
Database Singleton Object
database.js is used throughout the app to access the db object. Using mongodb
native drivers the db object contains a pool of connections that are used to
make requests to the db. To use this singleton object simply require it and
either call getDB() or setDB(). The idea is to use setDB in app.js just
after we connect to the db and receive the db object, then in any other file we
need the db require and call getDB
**/
var mongodb = require('mongodb');
var singleton = (function() {
var instance; //Singleton Instance
function init() {
var _db; //Instance db object (private)
//Other private variables and function can be declared here
return {
//Gets the instance's db object
getDB: function() {
return _db;
},
//Sets the instance's db object
setDB: function(db) {
_db = db;
}
//Other public variables and methods can be declared here
};
}
return {
//getInstance returns the singleton instance or creates a new one if
//not present
getInstance: function() {
if (!instance) {
instance = init();
}
return instance;
}
};
})();
module.exports = singleton.getInstance();

NodeJS - MongoDB: use an opening connection

It is better to open a new connection or re-use ? when using module, because I'm used to separate my code into several files.
a.js
module.exports = function (req, res) {
new mongodb.... (err, db) { // open a connection
b(function (err, result) {
db.close(); // close the connection
res.send(result);
});
});
};
b.js
// re-open a connection ? or take the connection of "a.js" ? (passing "db")
When asynchronous, one must be careful to continue using the same connection (socket). This ensures that the next operation will not begin until after the write completes.
Thanks !
When you require('somemodule') and then require it again a second time, it will use the ALREADY loaded instance. This lets you create singletons quite easily.
So - inside of sharedmongo.js:
var mongo = require('mongodb');
// this variable will be used to hold the singleton connection
var mongoCollection = null;
var getMongoConnection = function(readyCallback) {
if (mongoCollection) {
readyCallback(null, mongoCollection);
return;
}
// get the connection
var server = new mongo.Server('127.0.0.1', 27017, {
auto_reconnect: true
});
// get a handle on the database
var db = new mongo.Db('squares', server);
db.open(function(error, databaseConnection) {
databaseConnection.createCollection('testCollection', function(error, collection) {
if (!error) {
mongoCollection = collection;
}
// now we have a connection
if (readyCallback) readyCallback(error, mongoCollection);
});
});
};
module.exports = getMongoConnection;
Then inside of a.js:
var getMongoConnection = require('./sharedmongo.js');
var b = require('./b.js');
module.exports = function (req, res) {
getMongoConnection(function(error, connection){
// you can use the Mongo connection inside of a here
// pass control to b - you don't need to pass the mongo
b(req, res);
})
}
And inside of b.js:
var getMongoConnection = require('./sharedmongo.js');
module.exports = function (req, res) {
getMongoConnection(function(error, connection){
// do something else here
})
}
The idea is when both a.js and b.js call getMongoCollection, the first time it will connect, and the second time it will return the already connected one. This way it ensure you are using the same connection (socket).

Resources