var cluster = require('cluster') , numCPUs = require('os').cpus().length , http = require('http') , url = require('url') , Sequelize = require('sequelize') , mysql = require('mysql') , async = require('async') , Mongoose = require('mongoose') , conn = Mongoose.connect('mongodb://127.0.0.1/hello_world') , MongoClient = require('mongodb').MongoClient , Handlebars = require('handlebars'); // MongoDB Raw Setup var mongodbCollections = { World: null, Fortune: null }; MongoClient.connect('mongodb://127.0.0.1/hello_world?maxPoolSize=5', function (err, db) { if (err) { throw err; } mongodbCollections.World = db.collection('world'); mongodbCollections.Fortune = db.collection('fortune'); }); // MySQL Raw Setup var connection = mysql.createConnection({ host : '127.0.0.1', user : 'benchmarkdbuser', password : 'benchmarkdbpass', database : 'hello_world' }); connection.connect(); // Mongoose Setup var WorldSchema = new Mongoose.Schema({ id : Number, randomNumber: Number }, { collection: 'world' }); var FortuneSchema = new Mongoose.Schema({ id: Number, message: String }, { collection: 'fortune' }); var mongoose = { World: conn.model('World', WorldSchema), Fortune: conn.model('Fortune', FortuneSchema) } // Sequelize Setup var sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', { host: '127.0.0.1', dialect: 'mysql', logging: false }); var World = sequelize.define('World', { id: { type: 'Sequelize.INTEGER' }, randomNumber: { type: 'Sequelize.INTEGER' } }, { timestamps: false, freezeTableName: true }); var Fortune = sequelize.define('Fortune', { id: { type: 'Sequelize.INTEGER' }, message: { type: 'Sequelize.STRING' } }, { timestamps: false, freezeTableName: true }); // Fortunes template via handlebars setup var fortunesTemplate = Handlebars.compile([ "", "", "Fortunes", "", "", "", "", "", "", "{{#fortunes}}", "", "", "", "", "{{/fortunes}}", "
idmessage
{{id}}{{message}}
", "", "" ].join('')); // Helper functions function getRandomNumber() { return Math.floor(Math.random() * 10000) + 1; } function fillArray(value, len) { var arr = []; for (var i = 0; i < len; i++) { arr.push(value); } return arr; } function addTfbHeaders(res, headerType) { var headers = { 'Server': 'Node' } if (headerType === 'plain') { headers['Content-Type'] = 'text/plain; charset=UTF-8'; } else if (headerType === 'json') { headers['Content-Type'] = 'application/json'; } else if (headerType === 'html') { headers['Content-Type'] = 'text/html; cherset=UTF-8'; } res.writeHead(200, headers); } // Mongoose Query Functions function mongooseRandomWorld(callback) { mongoose.World.findOne({ id: getRandomNumber() }).exec(function (err, world) { callback(err, world); }); } function mongooseGetAllFortunes(callback) { mongoose.Fortune.find({}).exec(function (err, fortunes) { callback(err, fortunes); }); } // MongoDB-Raw Query Functions function mongodbDriverQuery(callback) { mongodbCollections.World.findOne({ id: getRandomNumber() }, function(err, world) { world._id = undefined; // remove _id from query response callback(err, world); }); } function mongodbGetAllFortunes(callback) { mongodbCollections.Fortune.find().toArray(function (err, fortunes) { callback(err, fortunes); }) } function mongodbDriverUpdateQuery(callback) { mongodbCollections.World.findAndModify({ id: getRandomNumber() }, [['_id','asc']], { $set: {randomNumber: getRandomNumber()} }, {}, function(err, world) { world.value._id = undefined; // remove _id from query response callback(err, world.value); }); } // Sequelize Query Functions function sequelizeQuery(callback) { World.findOne({ where: { id: Math.floor(Math.random() * 10000) + 1} } ).complete(callback); } // MySQL-Raw Query Functions function mysqlQuery(callback) { connection.query("SELECT * FROM world WHERE id = " + getRandomNumber(), function (err, rows, fields) { if (err) { throw err; } callback(null, rows[0]); }); } function mysqlUpdateQuery(callback) { connection.query("SELECT * FROM world WHERE id = " + getRandomNumber(), function (err, rows, fields) { if (err) { throw err; } rows[0].randomNumber = getRandomNumber(); var updateQuery = "UPDATE world SET randomNumber = " + rows[0].randomNumber + " WHERE id = " + rows[0]['id']; connection.query(updateQuery, function (err, result) { if (err) { throw err; } callback(null, rows[0]); }); }); } var GREETING = "Hello, World!"; var HELLO_OBJ = { message: GREETING }; var ADDITIONAL_FORTUNE = { id: 0, message: 'Additional fortune added at request time.' }; var responses = { jsonSerialization: function (req, res) { addTfbHeaders(res, 'json'); res.end(JSON.stringify(HELLO_OBJ)); }, plaintext: function (req, res) { addTfbHeaders(res, 'plain'); res.end(GREETING); }, mongooseSingleQuery: function (req, res) { mongooseRandomWorld(function (err, result) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(result)); }) }, mongooseMultipleQueries: function (queries, req, res) { var queryFunctions = fillArray(mongooseRandomWorld, queries) async.parallel(queryFunctions, function (err, results) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(results)); }); }, mongooseFortunes: function (req, res) { mongooseGetAllFortunes(function (err, fortunes) { if (err) { throw err; } fortunes.push(ADDITIONAL_FORTUNE); fortunes.sort(function (a, b) { return a.message.localeCompare(b.message); }); addTfbHeaders(res, 'html'); res.end(fortunesTemplate({ fortunes: fortunes })) }); }, mongooseUpdates: function (queries, req, res) { var selectFunctions = fillArray(mongooseRandomWorld, queries); async.parallel(selectFunctions, function (err, worlds) { var updateFunctions = []; for (var i = 0; i < queries; i++) { (function (i) { updateFunctions.push(function (callback) { worlds[i].randomNumber = getRandomNumber(); mongoose.World.update({ id: worlds[i].id }, { randomNumber: worlds[i].randomNumber }, callback); }); })(i); } async.parallel(updateFunctions, function (err, results) { if (err) { throw err; } addTfbHeaders(res, 'json'); // results does not have updated document information // if no err was found and thrown: all updates succeeded res.end(JSON.stringify(worlds)); }); }); }, mongodbSingleQuery: function (req, res) { mongodbDriverQuery(function (err, result) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(result)); }); }, mongodbMultipleQueries: function (queries, req, res) { var queryFunctions = fillArray(mongodbDriverQuery, queries); async.parallel(queryFunctions, function (err, results) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(results)); }); }, mongodbFortunes: function (req, res) { mongodbGetAllFortunes(function (err, fortunes) { if (err) { throw err; } fortunes.push(ADDITIONAL_FORTUNE); fortunes.sort(function (a, b) { return a.message.localeCompare(b.message); }); addTfbHeaders(res, 'html'); res.end(fortunesTemplate({ fortunes: fortunes })); }); }, mongodbUpdates: function (queries, req, res) { var queryFunctions = fillArray(mongodbDriverUpdateQuery, queries); async.parallel(queryFunctions, function (err, results) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(results)); }); }, sequelizeSingleQuery: function (req, res) { sequelizeQuery(function (err, result) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(result)); }); }, sequelizeMultipleQueries: function (queries, req, res) { var queryFunctions = fillArray(sequelizeQuery, queries); async.parallel(queryFunctions, function (err, results) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(results)); }); }, sequelizeFortunes: function (req, res) { Fortune.findAll().complete(function (err, fortunes) { fortunes.push(ADDITIONAL_FORTUNE); fortunes.sort(function (a, b) { return a.message.localeCompare(b.message); }); addTfbHeaders(res, 'html'); res.end(fortunesTemplate({ fortunes: fortunes })); }); }, sequelizeUpdates: function (queries, req, res) { var selectFunctions = fillArray(sequelizeQuery, queries); async.parallel(selectFunctions, function (err, worlds) { var updateFunctions = []; for (var i = 0; i < queries; i++) { (function (i) { updateFunctions.push(function (callback) { worlds[i].randomNumber = getRandomNumber(); worlds[i].save().complete(callback); }); })(i); } async.parallel(updateFunctions, function (err, updates) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(updates)); }); }); }, mysqlSingleQuery: function (req, res) { mysqlQuery(function (err, result) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(result)); }); }, mysqlMultipleQueries: function (queries, req, res) { var queryFunctions = fillArray(mysqlQuery, queries); async.parallel(queryFunctions, function (err, results) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(results)); }); }, mysqlUpdates: function (queries, req, res) { var queryFunctions = fillArray(mysqlUpdateQuery, queries); async.parallel(queryFunctions, function (err, results) { if (err) { throw err; } addTfbHeaders(res, 'json'); res.end(JSON.stringify(results)); }); }, routeNotImplemented: function (req, res) { res.writeHead(501, {'Content-Type': 'text/plain; charset=UTF-8'}); var reason = { reason: "`" + req.url + "` is not an implemented route" }; res.end(JSON.stringify(reason)); } }; if (cluster.isMaster) { // Fork workers. for (var i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', function (worker, code, signal) { console.log('worker ' + worker.pid + ' died'); }); } else { http.createServer(function (req, res) { var values = url.parse(req.url, true); var route = values.pathname; // Basic routes, no db required if (route === '/json') { return responses.jsonSerialization(req, res); } else if (route === '/plaintext') { return responses.plaintext(req, res); // No queries parameter required } else if (route === '/mongoose/db') { return responses.mongooseSingleQuery(req, res); } else if (route === '/mongoose/fortunes') { return responses.mongooseFortunes(req, res); } else if (route === '/mongodb/db') { return responses.mongodbSingleQuery(req, res); } else if (route === '/mongodb/fortunes') { return responses.mongodbFortunes(req, res); } else if (route === '/sequelize/db') { return responses.sequelizeSingleQuery(req, res); } else if (route === '/sequelize/fortunes') { return responses.sequelizeFortunes(req, res); } else if (route === '/mysql/db') { return responses.mysqlSingleQuery(req, res); } else { var queries = isNaN(values.query.queries) ? 1 : parseInt(values.query.queries, 10); queries = Math.min(Math.max(queries, 1), 500); if (route === '/mongoose/queries') { return responses.mongooseMultipleQueries(queries, req, res); } else if (route === '/mongoose/updates') { return responses.mongooseUpdates(queries, req, res); } else if (route === '/mongodb/queries') { return responses.mongodbMultipleQueries(queries, req, res); } else if (route === '/mongodb/updates') { return responses.mongodbUpdates(queries, req, res); } else if (route === '/sequelize/queries') { return responses.sequelizeMultipleQueries(queries, req, res); } else if (route === '/sequelize/updates') { return responses.sequelizeUpdates(queries, req, res); } else if (route === '/mysql/queries') { return responses.mysqlMultipleQueries(queries, req, res); } else if (route === '/mysql/updates') { return responses.mysqlUpdates(queries, req, res); } else { return responses.routeNotImplemented(req, res); } } }).listen(8080, function() { console.log("NodeJS worker listening on port 8080"); }); }