MongoDB singleton connection in Node.Js
afshinm.name
afshinm.name
- Query all collections at startup time and attach them to your singleton so you can just call MongoDbSingleton[collection].find(). This way you only call your collections once and gain lot in speed for subsequent requests.
- Since you are querying all your collections, you might as well use .ensureIndex() by then to make sure your application won't insert two users with the same e-mail.
- Now that you have sane collections, you can rid your application code of one "useless" instruction: give each collection a method that wraps the call to .find() in another function that will perform the .toArray() for you in the background. (This is NOT removing the step, it is merely moving it elsewhere for code readability).
- Play a bit more with your new singleton: you know how to craft a BSON ObjectId from an hexstring? Simple, call "mongodb.BSONPure.ObjectID.createFromHexString()". Okay, too long, why not have a method to wrap that in your singleton?
- All these function (err [, data]) flying around... Why not have a single function that will always be called to check for "err" existence and output error and trace?
I've been using most (if not all) of those tips in my own MongoDb wrapper, both in professionnal apps and side-projets and it's made my MongoDb use much more simpler. Hit me up if you want some more tips ;)
I'll clean it up a bit before. It should appear in https://github.com/joshleaves and https://npmjs.org/~joshleaves by the end of the day :)
What you do need is a state variable that indicates if the connection is uninitialized, loading, or ready. And if it is in the loading state you need to have a list of callbacks so that concurrent requests can be added to the list.
if (connectionInstance) {
setTimeout(function(){callback(connectionInstance)}, 0);
return;
}
That way your async functions will always behave the same way.Is there any reason to not do it my way? I never see it shown that way in examples so I wonder if I'm missing something.
also some collections could be just a waste of ram for your app
As strange as it may sound...there's no need to update them (or my apps have been lucking it out for the last six months?).
When you use less than 20 collections, this is not the kind of thing that'll become a "waste of ram". You could try a LRU to remove unused collections and get them later...but you would still lose more RAM because of the LRU manager...
Also, I thought the 'official' mongodb library already does connection pooling, or is this something different? Just curious.
One big tip for mongoose is to use the .lean() function on your queries whenever you don't need to use any special mongoose features (statics, virtuals, etc), which returns a standard JS Object instead of a mongoose collection instance. It can be quite a bit faster!
The "Attach each collection to your MongoDbSingleton as MongoDbSingleton[Collection]" trick was actually taken from Mongoose (which uses MongooseSingleton[Model] instead).
Though the overhead can be rather minimal, it still exists:
- Connect to the database
- Query the Collection
- Make a request
- Close the connection
Also, these four separate request are asynchronous, which means you could end up with X web requests having connected to the DB but not having a collection, Y requests having a collection and N requests waiting to be closed.
This is not a real problem (as long as you don't have 4 servers connecting to the same Db with 65 thousand connections each :-) ) but having multiple CONCURRENT connections to the same server is pretty useless in a SINGLE-THREADED context like a Node.js app.
Now, if you were to use PHP, that's a different matter...
With that said, you shouldn't create a connection per request, you should use a connection pool.
You may be thinking you can fire up multiple request at the same time (ie: fire the requests, let the I/O do the job, get all your requests back) but since you are in a single-threaded environment, you do exactly the same as I/O on a single connection (plus the overhead of opening new sockets and establishing hands shaking with the server).
And yes, that is exactly what I am thinking. More database connections = more concurrent database requests. I don't understand your reasoning about being in a single threaded environment. You made a statement but you haven't explained it.
From what I can see, this problem is simply serial vs parallel. Is there something I'm missing? I don't see how the fact that node.js is single threaded changes the situation since the database requests aren't working within the node.js context.
Edit: sorry, my previous reply may have been confusing. I was responding to your comment that having multiple connections is useless in a single-threaded environment. I was not suggesting that it may be helpful for the OP to make a new connection for each incoming http request.
In both cases, the same process of reading/writing to one or more connection sockets happen, you send and receive the same content to MongoDb: you can not send twice as much queries, you're gonna send a query, THEN you're gonna send another, then you get an answer THEN you get another answer. Everything happens in the same sequence.
Also take in account MongoDb's own concurrency properties.
The balance is between how big your pool is and how many messages are in flight. In the future this will change as MongoDB moves to some sort of eventloop or multiplexing of threads to sockets. But for the moment you have to strike a balance between operations in flight and how many sockets you open on the server.
Another thing to keep in mind is that the more socket connections you open on the mongodb server the more memory needs to allocated on the heap and the more contexts switches between threads will happen.
The Singleton Pattern is, as its name implies, a pattern. It's not a "good pattern", it's not an "anti pattern",... It is ONLY a pattern whose rightful use relies upon the developper.
Is a singleton justified in this case where the developper wants ONE connection object to be used across all his application? I don't see why not.
Would a non-singleton object that would be re-instancied upon each file/request/w/ever be better suited? I beg to differ.
But if your comment can teach us one thing, it's that for every pattern and every HN comment, the malice resides only between keyboard and chair.