Why isn't my Node script exiting? (utilising Monk) - node.js

I'm writing a library to abstract my data layer (it's going to use a mix of Mongo and Memcached). I've been testing Monk and can't figure out why the below script isn't finishing:
mongo = require("monk")("mongodb://#{options.mongodb.username}:#{options.mongodb.password}##{options.mongodb.hostname}:#{options.mongodb.port}/#{options.mongodb.database}")
users = mongo.get("users")
find = users.findById 12345
find.complete (err, doc) ->
console.dir doc
console.dir err
It's returning the document to the log, { _id: 12345, foo: "bar" }, successfully but not completing when run using node test.js. Why is this?

The reason the script stays alive is because the connection to MongoDB is still open. If you call mongo.close(); that should close the connection and provided you have nothing else keeping the event loop alive (e.g. network connections, timers, etc), then your script should terminate.

Related

How does async work in Express?

I found the following on the ExpressJS guide:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'dbuser',
password : 's3kreee7'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
if (err) throw err;
console.log('The solution is: ', rows[0].solution);
});
connection.end();
Isn't this supposed to be bad practice? The way I see it, it is possible for the connection to end before the query can be executed. Wouldn't that give an error?
As stated here :
Every method you invoke on a connection is queued and executed in sequence.
Closing the connection is done using end() which makes sure all remaining queries are executed before sending a quit packet to the mysql server.
So even though the call to the end() method can be made before the query has completed, it won't actually be executed until the query has finished executing.
This has to do more with the mysql package than NodeJS itself.
Your question How does async work in Express? and Isn't this supposed to be bad practice? can be answered in many ways, but for clarity I would like to explain that It depends !!!!
It generally is very bad practice, assuming you don't know the actual implementation.
If the the implementation is really simple, where it does exactly what you ask -- i.e. closes or ends the connection when end is executed then it could lead to rather ugly race conditions where it may or may not work depending on the load of the machines.
However, a clever implementation that does reference counting -- that is the end does not actually close the connection but just sets a flag to say -- "when last callback is done then close" -- then it may work.
If the mysql connector it implemented using reference counting then this may well work fine -- but that is not the same as saying that it is good practice for everything you find as a plugin.

Simulating Failure to Access Mongodb

I create an Express.js library that uses the official Node.js driver for its Mongodb operations.
I'm currently in the process of writing unit tests and I want to simulate failures to access the database in order to ensure:
The library acknowledges failure cases (handles the error)
Makes the right error callbacks and fires the proper events.
I want the tests to run cross-platform, preferably without having to shut down or start the database with special parameters.
Looking at the reference for commands, the sleep command seems to do almost exactly what I want, but the waiting time in seconds in pretty long, plus it is flagged as for internal use only and you need to fire the database with a special parameter for it to work. The forceerror command looks like another good one, but again, it's listed for internal use only and the description is vague to say the least.
I am wondering if there is any recommended (preferably not overly hackish) way of doing this.
Here, it requires superuser privileges for the node process executing this script to send signals to the MongoDB process without having spawned it, but it is the best I found so far to simulate unresponsiveness:
var MongoDB = require('mongodb');
MongoDB.MongoClient.connect("mongodb://localhost:27017/SomeDB", {'server': {'socketOptions': {'connectTimeoutMS': 50, 'socketTimeoutMS': 50}}}, function(Err, DB) {
if(Err)
{
console.log(Err);
}
else
{
DB.command({'serverStatus': 1}, function(Err, Result) {
if(Err)
{
console.log(Err)
}
else
{
process.kill(Result.pid, 'SIGSTOP');
//Put testing logic to test unresponsiveness
process.kill(Result.pid, 'SIGCONT');
DB.close();
}
});
}
});
Edit:
If your testing logic crashes on Linux, you can resume the MongoDB process manually on the shell by executing:
kill -CONT PID
Where PID is the process id of the MongoDB process.

Running Knex queries taking longer in files vs REPL

I'm trying to seed some data to a database:
// seed.js
var Knex = require("knex");
var database = require("./config").database;
var knex = Knex.initialize(database);
knex("users").insert({
first_name: "John",
last_name: "Doe"
}).exec(function (err, id) {
console.log("Fulfilled", id);
});
// returns [ 1 ]
My problem is that if I put this in a file and running it using node seed.js, it executes the console.log then it seems to be waiting longer (about 20 seconds) before it returns the shell prompt. Doing the same in REPL where the result is instantaneous.
Is there a reason why my code will wait 20 seconds before it shows me the command prompt again if I run it in shell vs executing this in a REPL?
by default node won't give you the console back until the script is totally completely done, while inside the node repl it gives back the repl as soon as the last line stops blocking.
In the repl you actually get focus back before the console.log prints, while with the script it also has to wait for stuff to close up, you can speed it along by adding process.exit(0) after the console.log.

Cancel previous MongoDB operation from the same client

I have a MongoDB collection of 3257477 cities, and I'm using Mongoose on NodeJS to access it. I'm making requests to it repeatedly (once per 500ms). Requests are usually answered very quickly. However, when I make a bad typo the query takes a long time and requests start to pile up until the initial request is answered. Here are some logs I collected of requests and responses:
21:48:50 started query for "new"
21:48:50 finished query for "new"
21:48:52 started query for "newj ljl" // blockage
21:48:54 started query for "newj"
21:48:55 started query for "new"
21:48:57 started query for "new ye"
21:48:59 started query for "new york"
21:49:08 finished query for "newj ljl" // blockage removed, quick queries flood in
21:49:08 finished query for "new"
21:49:08 finished query for "new york"
21:49:08 finished query for "new ye"
21:49:23 finished query for "newj"
I'm able to cancel the requests made by the client so I'm not worried about queries coming back in the wrong order. And I'm not interested in how to make that query faster at this point, since queries for actual correct spellings are quick.
I'm wondering how a new request can cancel an old request that was made by the same client. In other words "newj ljl" gets canceled when "newj" arrives, "newj" gets canceled when "new" arrives, and so on. If it's just going to be thrown out, why tie up the database?
Is there a proper way to do this?
Update:
I'm aware of db.currentOp().inprog and I'm thinking I can use the client property of the documents within that array to know whether it's a repeat request, but I can't quite figure out how to access that from Mongoose. I'm also not sure when to do that, or how I know which request was spawned from this client (and therefore which to cancel). I'd like an actual code example using Mongoose, or the native NodeJS MongoDB driver if possible!
Here's some sample code to go off of:
models.City.find({ ... })
.exec(function (err, cities) {
});
Below is what I came up with to solve the issue.
I can easily do db.currentOp().inprog and db.killOp() from the Mongo shell, but I really need this to happen automatically, when it needs to, from Mongoose. Since you can reference the MongoDB driver using require('mongoose').connection.db, you can execute those commands by doing "queries" on the following collections:
db.collection('$cmd.sys.inprog');
db.collection('$cmd.sys.killop');
The full solution:
var db = require('mongoose').connection.db,
// get the client IP address
ip = request.headers['x-forwarded-for'] ||
request.connection.remoteAddress ||
request.socket.remoteAddress ||
request.connection.socket.remoteAddress;
// same thing as db.currentOp().inprog
db.collection('$cmd.sys.inprog').findOne(function (err, data) {
if (err) throw err;
data.inprog.filter(function (op) {
// get the operation's client IP address without the port
return ip == op.client.split(':')[0];
}).forEach(function(op){
// same thing as db.killOp()
db.collection('$cmd.sys.killop')
.findOne({ 'op': op.opid }, function (err, data) {
if (err) throw err;
});
});
// start the new cities query
models.City.find({ ... })
.exec(function (err, cities) {
});
});
Helpful links:
https://groups.google.com/forum/#!topic/mongodb-user/1wFp7AqWnM4
drop database with mongoose
How to determine a user's IP address in node
You can try using db.killOp()
http://docs.mongodb.org/manual/reference/method/db.killOp/#db.killOp
UPDATE: You can get the list of current operations from db.currentOp() and identify the operation to be cancelled by matching fields like op, query and client
http://docs.mongodb.org/manual/reference/method/db.currentOp/#db.currentOp
You can definitely do this with killop, and the above solution looks like it could work for the problem as stated. However, I think it may be worthwhile to dig a bit deeper.
The fact that you have a noticeably slow query when you've got a query that's going to return no results seems unusual. That reeks of a full collection scan. The questions to ask are, first, do you have indices set up, and second, are you querying with a general regex? MongoDB doesn't really handle regex searches like { "name" : /.*new york.*/ } particularly well.
Also, the whole "send an http request every time the user hits a key" approach is simple and elegant, but also causes some unnecessary server load. Perhaps a search button or a client-side timeout where you only send a request if a user hasn't hit a key for 1 second could help alleviate the need for the killop approach.

Mongoose and commander

I'm writing some scripts for some command-line manipulation of Mongoose models with commander.js (eventually, I'd like to run these tools using Cron).
Now, I've written several scripts with commander and they all work fine, but if I connect to the MongoDB database using mongoose, they script just hangs after it's done. Now, I figured the database connection is keeping node alive, so I added a mongoose.disconnect() line and it still hangs.
The only thing I found that allows me to shutdown is to use process.exit(), but I'm reluctant to just terminate the process. Is there something in particular that I should do to trigger a graceful shutdown?
My reading of the API docs implies that .disconnect() must be given a callback function. It looks like it's called for each that's disconnected and may be passed an error.
There is a check in the code to make sure it's not called if it doesn't exist when things work out, but that check isn't being run on errors, so if Mongoose received an error message from the MongoDB client, it may be leaving a connection open and that's why it's not stopping execution.
If you're only opening a single connection to the database, you may just want to call [Connection object].close() since that function correctly inserts a no-op "callback" if no callback is given, and looks like it will correctly destruct things.
(The more I look into Mongoose, the more I want to just write a thin wrapper around the MongoDB client so I don't have to deal with Mongoose's "help.")
I use the async "Series" to perform operations and then call mongoose.connection.close() on completion. It prevents callback hell and allows you to neatly perform operations either one at a time or parallel followed by a function when all the other methods have completed. I use it all the time for scripts that require mongoose but are meant to terminate after all mongoose operations are finished.
Shutdown the node program directly is hiding the symptoms, not fixing the problem!
I finally isolated the problem and found it to be with Mongoose schema definitions. If you try to shutdown the connection too soon after Mongoose schemas are defined1, the application hangs and eventually produces some weird MongDB-related error.
Adding a small timeout before running the program.parse(argv) line to run the commander application fixes the problem. Just wrap the code like so:
var program = require('commander')
, mongoose = require('mongoose')
, models = null
;
// Define command line syntax.
program
.command(...)
;
mongoose.connect(
..., // connection parameters.
function() {
// connected to database, defined schemas.
models = require('./models');
// Wait 1 second before running the application code.
setTimeout(function(){
program.parser(process.argv);
}, 1000);
}
);
1: This is my initial interpretation, I have not (yet) extensively tested this theory. However, removing Mongoose schema definitions from the application successfully prevents the application from hanging.
Actually, just using process.nextTick() instead of the setTimeout() call fixes the situation nicely!

Resources