Browse Source

Merge branch 'hapi' of https://github.com/avaly/FrameworkBenchmarks into avaly-hapi

Patrick Falls 12 years ago
parent
commit
985f0e45db
7 changed files with 356 additions and 0 deletions
  1. 54 0
      hapi/README.md
  2. 0 0
      hapi/__init__.py
  3. 195 0
      hapi/app.js
  4. 28 0
      hapi/benchmark_config
  5. 12 0
      hapi/package.json
  6. 46 0
      hapi/setup.py
  7. 21 0
      hapi/views/fortunes.handlebars

+ 54 - 0
hapi/README.md

@@ -0,0 +1,54 @@
+# Hapi Benchmarking Test
+
+This is the Hapi portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### JSON Encoding Test
+
+* [JSON test source](app.js)
+
+### Data-Store/Database Mapping Test
+
+* [DB test controller/model](app.js)
+
+## Infrastructure Software Versions
+The tests were run with:
+* [Node.js v0.10.0](http://nodejs.org/)
+* [Hapi 1.2.0](http://spumko.github.io/)
+
+## Resources
+* http://nodejs.org/api/cluster.html
+
+## Test URLs
+### JSON Encoding Test
+
+http://localhost:8080/json
+
+### Data-Store/Database Mapping Test
+
+MongoDB:
+http://localhost:8080/mongoose/
+
+MySQL:
+http://localhost:8080/mysql-orm/
+
+### Variable Query Test
+
+MongoDB:
+http://localhost:8080/mongoose/2
+
+MySQL:
+http://localhost:8080/mysql-orm/2
+
+### Fortune Test
+
+MySQL:
+http://localhost:8080/fortune
+
+### Database Update Test
+
+MongoDB:
+http://localhost:8080/mongoose-update/2
+
+MySQL:
+http://localhost:8080/mysql-orm-update/2
+

+ 0 - 0
hapi/__init__.py


+ 195 - 0
hapi/app.js

@@ -0,0 +1,195 @@
+/**
+ * Module dependencies.
+ */
+
+var cluster = require('cluster'),
+	numCPUs = require('os').cpus().length,
+	windows = require('os').platform() == 'win32',
+	Hapi = require('hapi'),
+	mongoose = require('mongoose'),
+	async = require('async'),
+	conn = mongoose.connect('mongodb://localhost/hello_world'),
+	connMap = { user: 'benchmarkdbuser', password: 'benchmarkdbpass', database: 'hello_world', host: 'localhost' };
+
+var WorldSchema = new mongoose.Schema({
+		id          : Number,
+		randomNumber: Number
+	}, {
+		collection: 'world'
+	}),
+	MWorld = conn.model('World', WorldSchema);
+
+if (!windows) {
+	var Mapper = require('mapper');
+	Mapper.connect(connMap, {verbose: false, strict: false});
+	var World = Mapper.map('World', 'id', 'randomNumber');
+	var Fortune = Mapper.map('Fortune', 'id', 'message');
+}
+
+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 {
+	var server = module.exports = Hapi.createServer(null, 8080, {
+		views: {
+			engines: {
+				handlebars: 'handlebars'
+			},
+			path: __dirname + '/views'
+		}
+	});
+
+	server.route({
+		method: 'GET',
+		path: '/json',
+		handler: function(req) {
+			req.reply({ message: 'Hello World!' })
+		}
+	});
+
+	server.route({
+		method: 'GET',
+		path: '/mongoose/{queries?}',
+		handler: function(req){
+			var queries = req.params.queries || 1,
+				queryFunctions = [];
+
+			queries = Math.min(Math.max(queries, 1), 500);
+
+			for (var i = 1; i <= queries; i++) {
+				queryFunctions.push(function(callback){
+					MWorld.findOne({ id: (Math.floor(Math.random() * 10000) + 1) }).exec(callback);
+				});
+			}
+
+			async.parallel(queryFunctions, function(err, results){
+				req.reply(results).header('Server', 'hapi');
+			});
+		}
+	});
+
+	server.route({
+		method: 'GET',
+		path: '/mysql-orm/{queries?}',
+		handler: function(req){
+			if (windows) return req.reply(Hapi.error.internal('Not supported on windows'));
+
+			var queries = req.params.queries || 1,
+				queryFunctions = [];
+
+			queries = Math.min(Math.max(queries, 1), 500);
+
+			for (var i = 1; i <= queries; i++) {
+				queryFunctions.push(function(callback){
+					World.findById(Math.floor(Math.random() * 10000) + 1, callback);
+				});
+			}
+
+			async.parallel(queryFunctions, function(err, results){
+				req.reply(results).header('Server', 'hapi');
+			});
+		}
+	});
+
+	server.route({
+		method: 'GET',
+		path: '/fortune',
+		handler: function(req){
+			if (windows) return req.reply(Hapi.error.internal('Not supported on windows'));
+
+			Fortune.all(function(err, fortunes){
+				fortunes.push({
+					id: 0,
+					message: 'Additional fortune added at request time.'
+				});
+				fortunes.sort(function(a, b){
+					return (a.message < b.message) ? -1 : 1;
+				});
+
+				req.reply.view('fortunes.handlebars', {
+					fortunes: fortunes
+				}).header('Server', 'hapi');
+			});
+		}
+	});
+
+	server.route({
+		method: 'GET',
+		path: '/mongoose-update/{queries?}',
+		handler: function(req){
+			var queries = req.params.queries || 1,
+				selectFunctions = [];
+
+			queries = Math.max(Math.min(queries, 500), 1);
+
+			for (var i = 1; i <= queries; i++) {
+				selectFunctions.push(function(callback){
+					MWorld.findOne({ id: Math.floor(Math.random() * 10000) + 1 }).exec(callback);
+				});
+			}
+
+			async.parallel(selectFunctions, function(err, worlds) {
+				var updateFunctions = [];
+
+				for (var i = 0; i < queries; i++) {
+					(function(i){
+						updateFunctions.push(function(callback){
+							worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
+							MWorld.update({
+								id: worlds[i]
+							}, {
+								randomNumber: worlds[i].randomNumber
+							}, callback);
+						});
+					})(i);
+				}
+
+				async.parallel(updateFunctions, function(err, updates) {
+					req.reply(worlds).header('Server', 'hapi');
+				});
+			});
+		}		
+	});
+
+	server.route({
+		method: 'GET',
+		path: '/mysql-orm-update/{queries?}',
+		handler: function(req){
+			var queries = req.params.queries || 1,
+				selectFunctions = [];
+
+			queries = Math.max(Math.min(queries, 500), 1);
+
+			for (var i = 1; i <= queries; i++) {
+				selectFunctions.push(function(callback){
+					World.findById(Math.floor(Math.random() * 10000) + 1, callback);
+				});
+			}
+
+			async.parallel(selectFunctions, function(err, worlds) {
+				var updateFunctions = [];
+
+				for (var i = 0; i < queries; i++) {
+					(function(i){
+						updateFunctions.push(function(callback){
+							worlds[i].randomNumber = Math.ceil(Math.random() * 10000);
+							World.save(worlds[i], callback);
+						});
+					})(i);
+				}
+
+				async.parallel(updateFunctions, function(err, updates) {
+					req.reply(worlds).header('Server', 'hapi');
+				});
+			});
+		}
+	});
+
+	server.start();
+}

+ 28 - 0
hapi/benchmark_config

@@ -0,0 +1,28 @@
+{
+  "framework": "hapi",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "port": 8080,
+      "sort": 118
+    },
+    "mongodb": {
+      "setup_file": "setup",
+      "db_url": "/mongoose/",
+      "query_url": "/mongoose/",
+      "update_url": "/mongoose-update/",
+      "port": 8080,
+      "sort": 119
+    },
+    "mysql": {
+      "setup_file": "setup",
+      "db_url": "/mysql-orm/",
+      "query_url": "/mysql-orm/",
+      "fortune_url": "/fortune",
+      "update_url": "/mysql-orm-update/",
+      "port": 8080,
+      "sort": 120
+    }
+  }]
+}

+ 12 - 0
hapi/package.json

@@ -0,0 +1,12 @@
+{
+	"name": "application-name",
+	"version": "0.0.1",
+	"private": true,
+	"dependencies": {
+		"hapi": "1.2.0",
+		"mongoose": "3.5.5",
+		"async": "0.2.5",
+		"mapper": "0.2.4-pre",
+		"handlebars": "1.0.11"
+	}
+}

+ 46 - 0
hapi/setup.py

@@ -0,0 +1,46 @@
+import subprocess
+import sys
+import setup_util
+import os
+
+def start(args):
+  setup_util.replace_text("hapi/app.js", "localhost", args.database_host)
+
+  try:
+    npm()
+    if os.name == 'nt':
+      subprocess.Popen("set NODE_ENV=production", shell=True)
+      subprocess.Popen("node app", shell=True, cwd="hapi")
+    else:
+      subprocess.Popen("NODE_ENV=production node app", shell=True, cwd="hapi")
+    return 0
+  except subprocess.CalledProcessError:
+    return 1
+
+def npm():
+  if os.name == 'nt':
+    subprocess.check_call("copy package.json package.json.dist /y > NUL", shell=True, cwd="hapi")
+    setup_util.replace_text("hapi/package.json", ".*mapper.*", "")
+
+  try:
+    subprocess.check_call("npm install", shell=True, cwd="hapi")
+  finally:
+    if os.name == 'nt':
+      subprocess.check_call("del package.json", shell=True, cwd="hapi")
+      subprocess.check_call("ren package.json.dist package.json", shell=True, cwd="hapi")
+
+def stop():
+  if os.name == 'nt':
+    subprocess.Popen("taskkill /f /im node.exe > NUL", shell=True)
+    return 0
+
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'node app' in line:
+      pid = int(line.split(None, 2)[1])
+      try:
+        os.kill(pid, 9)
+      except OSError:
+        pass
+  return 0

+ 21 - 0
hapi/views/fortunes.handlebars

@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>Fortunes</title>
+</head>
+
+<body>
+	<table>
+		<tr>
+			<th>id</th>
+			<th>message</th>
+		</tr>
+{{#fortunes}}
+		<tr>
+			<td>{{id}}</td>
+			<td>{{message}}</td>
+		</tr>
+{{/fortunes}}
+	</table>
+</body>
+</html>