Bläddra i källkod

upgrade to koa v2, copy everything from hapi (#2769)

* upgrade to koa2, copy everything from hapi

* fix fortunes

* fix getQueries

* still behind flag on 7.5.0

* add bluebird back, using map

* haps doesn't return the promises

* world promise

* render is a promise as well

* limit type to application/json to fix warning
Scott Cooper 8 år sedan
förälder
incheckning
31a123687a

+ 46 - 0
frameworks/JavaScript/koa/README.md

@@ -3,3 +3,49 @@
 This is the Koa portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
 Information about Koa can be found at https://github.com/koajs/koa
+
+### JSON Encoding Test
+
+* [JSON test source](app.js)
+
+### Data-Store/Database Mapping Test
+
+* [DB test controller/model](app.js)
+
+## Resources
+* http://nodejs.org/api/cluster.html
+
+## Test URLs
+### JSON Encoding Test
+
+http://TFB-server:8080/json
+
+### Data-Store/Database Mapping Test
+
+MongoDB:
+http://TFB-server:8080/mongoose/
+
+MySQL:
+http://TFB-server:8080/mysql-orm/
+
+### Variable Query Test
+
+MongoDB:
+http://TFB-server:8080/mongoose/2
+
+MySQL:
+http://TFB-server:8080/mysql-orm/2
+
+### Fortune Test
+
+MySQL:
+http://TFB-server:8080/fortune
+
+### Database Update Test
+
+MongoDB:
+http://TFB-server:8080/mongoose-update/2
+
+MySQL:
+http://TFB-server:8080/mysql-orm-update/2
+

+ 8 - 119
frameworks/JavaScript/koa/app.js

@@ -1,19 +1,5 @@
-const cluster = require('cluster'),
-  numCPUs = require('os').cpus().length;
-
-// Koa Deps
-const koa = require('koa'),
-  route = require('koa-route'),
-  handlebars = require('koa-handlebars'),
-  bodyParser = require('koa-bodyparser'),
-  override = require('koa-override');
-
-// Monk MongoDB Driver Deps
-const monk = require('monk'),
-  wrap = require('co-monk'),
-  db = monk('mongodb://TFB-database/hello_world'),
-  worlds = wrap(db.get('world')),
-  fortunes = wrap(db.get('fortune'));
+const cluster = require('cluster');
+const numCPUs = require('os').cpus().length;
 
 if (cluster.isMaster) {
   // Fork workers.
@@ -21,108 +7,11 @@ if (cluster.isMaster) {
     cluster.fork();
   }
 
-  cluster.on('exit', (worker, code, signal) =>
-    console.log('worker ' + worker.process.pid + ' died'));
+  console.log('Master starting ' + new Date().toISOString(" "));
+  cluster.on('exit', (worker, code, signal) => {
+    process.exit(1);
+  });
 } else {
-  const app = module.exports = koa();
-  app.use(bodyParser());
-  app.use(override());
-  app.use(handlebars({
-    // needed, otherwise missing dir err
-    partialsDir: "views"
-  }));
-
-  // routes
-  app.use(route.get('/json', jsonHandler));
-  app.use(route.get('/db', dbHandler));
-  app.use(route.get('/queries', queriesHandler));
-  app.use(route.get('/fortunes', fortuneHandler));
-  app.use(route.get('/updates', updateHandler));
-  app.use(route.get('/plaintext', textHandler));
-
-  // Helpers
-  const getRandomNumber = () => ~~(Math.random()*10000) + 1;
-
-  const validateParam = (param) => {
-    let numOfQueries = isNaN(param) ? 1 : param;
-    if (numOfQueries > 500) {
-      numOfQueries = 500;
-    } else if (numOfQueries < 1) {
-      numOfQueries = 1;
-    }
-    return numOfQueries;
-  };
-
-  // Query Helpers
-  function *worldUpdateQuery() {
-    const randomId = getRandomNumber();
-    const randomNumber = getRandomNumber();
-    const result = yield worlds.update(
-      {id: randomId}, 
-      {$set: {randomNumber: randomNumber}}
-    );
-    return {
-      id: randomId,
-      randomNumber: randomNumber
-    }
-  }
-
-  function *worldQuery() {
-    return yield worlds.findOne({id: getRandomNumber()}, '-_id');
-  }
-
-  function *fortunesQuery() {
-    return yield fortunes.find({}, '-_id');
-  }
-
-  // Route handlers
-  function *jsonHandler() {
-    this.set('Server', 'Koa');
-    this.body = {
-      message: "Hello, world!"
-    }
-  }
-
-  function *dbHandler() {
-    this.set('Server', 'Koa');
-    this.body = yield worldQuery;
-  }
-
-  function *queriesHandler() {
-    this.set('Server', 'Koa');
-    let numOfQueries = validateParam(this.query.queries);
-    const queries = [];
-    for (let i = 0; i < numOfQueries; i++) {
-      queries.push(worldQuery);
-    }
-    this.body = yield queries;
-  }
-
-  function *fortuneHandler() {
-    this.set('Server', 'Koa');
-    const fortunes = yield fortunesQuery;
-    fortunes.push({
-      id: 0,
-      message: 'Additional fortune added at request time.'
-    });
-    fortunes.sort((a, b) => (a.message < b.message) ? -1 : 1);
-    yield this.render("fortunes", {fortunes: fortunes});
-  }
-
-  function *updateHandler() {
-    this.set('Server', 'Koa');
-    const numOfUpdates = validateParam(this.query.queries);
-    const queries = [];
-    for (let i = 0; i < numOfUpdates; i++) {
-      queries.push(worldUpdateQuery);
-    }
-    this.body = yield queries;
-  }
-
-  function *textHandler() {
-    this.set('Server', 'Koa');
-    this.body = 'Hello, world!';
-  }
-
-  app.listen(8080);
+  // worker task
+  require('./create-server');
 }

+ 48 - 4
frameworks/JavaScript/koa/benchmark_config.json

@@ -4,11 +4,11 @@
     "default": {
       "setup_file": "setup",
       "json_url": "/json",
-      "db_url": "/db",
-      "query_url": "/queries?queries=",
-      "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=",
       "plaintext_url": "/plaintext",
+      "db_url": "/mongoose/db",
+      "query_url": "/mongoose/queries?queries=",
+      "fortune_url": "/mongoose/fortunes",
+      "update_url": "/mongoose/updates?queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
@@ -24,6 +24,50 @@
       "display_name": "koa",
       "notes": "",
       "versus": "nodejs"
+    },
+    "mysql": {
+      "setup_file": "setup",
+      "db_url": "/sequelize/db",
+      "query_url": "/sequelize/queries?queries=",
+      "fortune_url": "/sequelize/fortunes",
+      "update_url": "/sequelize/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MySQL",
+      "framework": "koa",
+      "language": "JavaScript",
+      "flavor": "NodeJS",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "koa",
+      "notes": "",
+      "versus": "nodejs"
+    },
+    "postgres": {
+      "setup_file": "setup",
+      "db_url": "/sequelize-pg/db",
+      "query_url": "/sequelize-pg/queries?queries=",
+      "fortune_url": "/sequelize-pg/fortunes",
+      "update_url": "/sequelize-pg/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "koa",
+      "language": "JavaScript",
+      "flavor": "NodeJS",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "koa",
+      "notes": "",
+      "versus": "nodejs"
     }
   }]
 }

+ 56 - 0
frameworks/JavaScript/koa/create-server.js

@@ -0,0 +1,56 @@
+const Koa = require('koa');
+const Router = require('koa-router');
+const hbs = require('koa-hbs');
+const bodyParser = require('koa-bodyparser');
+const handlebars = require('handlebars');
+
+const MongooseHandler = require('./handlers/mongoose');
+const SequelizeHandler = require('./handlers/sequelize');
+const SequelizePgHandler = require('./handlers/sequelize-postgres');
+
+
+const app = new Koa();
+const router = new Router();
+app.use(bodyParser());
+app.use(hbs.middleware({
+  handlebars: handlebars,
+  viewPath: __dirname + '/views'
+}));
+
+function JsonSerialization(ctx, next) {
+  ctx.set('Server', 'Koa');
+  ctx.type = 'application/json';
+  ctx.body = { message: 'Hello, World!' };
+  return next();
+}
+
+function Plaintext(ctx, next) {
+  ctx.set('Server', 'Koa');
+  ctx.type = 'text/plain';
+  ctx.body = 'Hello, World!';
+  return next();
+}
+
+router.get('/json', JsonSerialization);
+router.get('/plaintext', Plaintext);
+
+
+router.get('/mongoose/db', MongooseHandler.SingleQuery);
+router.get('/mongoose/queries', MongooseHandler.MultipleQueries);
+router.get('/mongoose/fortunes', MongooseHandler.Fortunes);
+router.get('/mongoose/updates', MongooseHandler.Updates);
+
+router.get('/sequelize/db', SequelizeHandler.SingleQuery);
+router.get('/sequelize/queries', SequelizeHandler.MultipleQueries);
+router.get('/sequelize/fortunes', SequelizeHandler.Fortunes);
+router.get('/sequelize/updates', SequelizeHandler.Updates);
+
+router.get('/sequelize-pg/db', SequelizePgHandler.SingleQuery);
+router.get('/sequelize-pg/queries', SequelizePgHandler.MultipleQueries);
+router.get('/sequelize-pg/fortunes', SequelizePgHandler.Fortunes);
+router.get('/sequelize-pg/updates', SequelizePgHandler.Updates);
+
+app.use(router.routes());
+const server = app.listen(8080);
+console.log('Worker started and listening on http://0.0.0.0:8080 ' 
+  + new Date().toISOString(" "));

+ 107 - 0
frameworks/JavaScript/koa/handlers/mongoose.js

@@ -0,0 +1,107 @@
+// Connects to MongoDB using the mongoose driver
+// Handles related routes
+
+const Promise = require('bluebird');
+const h = require('../helper');
+// Can treat mongoose library as one that supports Promises
+// these methods will then have "-Async" appended to them.
+const Mongoose = Promise.promisifyAll(require('mongoose'));
+const connection = Mongoose.connect('mongodb://TFB-database/hello_world');
+
+const WorldSchema = new Mongoose.Schema({
+    id :          Number,
+    randomNumber: Number
+  }, {
+    collection: 'world'
+  });
+const FortuneSchema = new Mongoose.Schema({
+    id:      Number,
+    message: String
+  }, {
+    collection: 'fortune'
+  });
+
+const Worlds = connection.model('World', WorldSchema);
+const Fortunes = connection.model('Fortune', FortuneSchema);
+
+const randomWorldPromise = () =>
+  Worlds.findOneAsync({ id: h.randomTfbNumber() })
+    .then((world) => world)
+    .catch((err) => process.exit(1));
+
+
+const promiseAllFortunes = () =>
+  Fortunes.findAsync({})
+    .then((fortunes) => fortunes)
+    .catch((err) => process.exit(1));
+
+const updateWorld = (world) =>
+  Worlds
+    .updateAsync(
+      { id: world.randomNumber },
+      { randomNumber: world.randomNumber }
+    )
+    .then((result) => world)
+    .catch((err) => process.exit(1));
+
+module.exports = {
+
+  SingleQuery: (ctx, next) => {
+    return randomWorldPromise()
+      .then((world) => {
+        ctx.set('Server', 'Koa');
+        ctx.type = 'application/json';
+        ctx.body = world;
+        return next();
+      });
+  },
+
+  MultipleQueries: (ctx, next) => {
+    const queries = h.getQueries(ctx);
+    const worldPromises = h.fillArray(randomWorldPromise(), queries);
+
+    return Promise
+      .all(worldPromises)
+      .then((worlds) => {
+        ctx.set('Server', 'Koa');
+        ctx.type = 'application/json';
+        ctx.body = worlds;
+        return next()
+      });
+  },
+
+  Fortunes: (ctx, next) => {
+    return promiseAllFortunes()
+      .then((fortunes) => {
+        fortunes.push(h.additionalFortune());
+        fortunes.sort((a, b) => a.message.localeCompare(b.message));
+      
+        ctx.type = 'text/html';
+        ctx.set('Server', 'Koa');
+        return ctx.render('fortunes', { fortunes: fortunes });
+      });
+  },
+
+  Updates: (ctx, next) => {
+    const queries = h.getQueries(ctx);
+    const worldPromises = [];
+
+    for (let i = 0; i < queries; i++) {
+      worldPromises.push(randomWorldPromise());
+    }
+
+    return Promise
+      .all(worldPromises)
+      .map((world) => {
+        world.randomNumber = h.randomTfbNumber();
+        return updateWorld(world);
+      })
+      .then((worlds) => {
+        ctx.set('Server', 'Koa');
+        ctx.type = 'application/json';
+        ctx.body = worlds;
+        return next();
+      });
+  }
+
+};

+ 109 - 0
frameworks/JavaScript/koa/handlers/sequelize-postgres.js

@@ -0,0 +1,109 @@
+// Connects to Postgres using the sequelize driver
+// Handles related routes
+const Promise = require('bluebird');
+const h = require('../helper');
+const Sequelize = require('sequelize');
+
+const sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+  host: 'TFB-database',
+  dialect: 'postgres',
+  logging: false
+});
+
+const Worlds = sequelize.define('World', {
+  id: {
+    type: 'Sequelize.INTEGER',
+    primaryKey: true
+  },
+  randomnumber: { type: 'Sequelize.INTEGER' }
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+
+const Fortunes = sequelize.define('Fortune', {
+  id: {
+    type: 'Sequelize.INTEGER',
+    primaryKey: true
+  },
+  message:      { type: 'Sequelize.STRING' }
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+
+const randomWorldPromise = () =>
+  Worlds.findOne({ where: { id: h.randomTfbNumber() } })
+    .then((results) => results)
+    .catch((err) => process.exit(1));
+
+module.exports = {
+
+  SingleQuery: (ctx, next) =>
+    randomWorldPromise().then((world) => {
+      ctx.set('Server', 'Koa');
+      ctx.type = 'application/json';
+      ctx.body = world;
+      return next();
+    }),
+
+  MultipleQueries: (ctx, next) => {
+    const queries = h.getQueries(ctx),
+      worldPromises = [];
+
+    for (let i = 0; i < queries; i++) {
+      worldPromises.push(randomWorldPromise());
+    }
+
+    return Promise.all(worldPromises)
+      .then((worlds) => {
+        ctx.set('Server', 'Koa');
+        ctx.type = 'application/json';
+        ctx.body = worlds;
+        return next();
+      });
+  },
+
+  Fortunes: (ctx, next) => {
+    return Fortunes.findAll().then((fortunes) => {
+      fortunes.push(h.additionalFortune());
+      fortunes.sort((a, b) => a.message.localeCompare(b.message));
+
+      ctx.set('Server', 'Koa');
+      ctx.type = 'text/html';
+      return ctx.render('fortunes', { fortunes });
+    }).catch((err) => process.exit(1));
+  },
+
+  Updates: (ctx, next) => {
+    const queries = h.getQueries(ctx),
+      worldPromises = [];
+
+    for (let i = 0; i < queries; i++) {
+      worldPromises.push(randomWorldPromise());
+    }
+
+    const worldUpdate = (world) => {
+      world.randomNumber = h.randomTfbNumber();
+
+      return Worlds.update(
+        {randomNumber: world.randomNumber},
+        {where: {id: world.id}}
+      )
+        .then((results) => world)
+        .catch((err) => process.exit(1));
+    };
+
+    return Promise
+      .all(worldPromises)
+      .map((world) => worldUpdate(world))
+      .then((updated) => {
+        ctx.set('Server', 'Koa');
+        ctx.type = 'application/json';
+        ctx.body = updated;
+        return next();
+      })
+      .catch((err) => process.exit(1));
+  }
+
+};

+ 109 - 0
frameworks/JavaScript/koa/handlers/sequelize.js

@@ -0,0 +1,109 @@
+// Connects to MySQL using the sequelize driver
+// Handles related routes
+const Promise = require('bluebird');
+const h = require('../helper');
+
+const Sequelize = require('sequelize');
+const sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+  host: 'TFB-database',
+  dialect: 'mysql',
+  logging: false
+});
+
+const Worlds = sequelize.define('World', {
+  id: {
+    type: 'Sequelize.INTEGER',
+    primaryKey: true
+  },
+  randomNumber: { type: 'Sequelize.INTEGER' }
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+
+const Fortunes = sequelize.define('Fortune', {
+  id: {
+    type: 'Sequelize.INTEGER',
+    primaryKey: true
+  },
+  message: { type: 'Sequelize.STRING' }
+}, {
+  timestamps: false,
+  freezeTableName: true
+});
+
+const randomWorldPromise = () =>
+  Worlds.findOne({ where: { id: h.randomTfbNumber() } })
+    .then((results) => results)
+    .catch((err) => process.exit(1));
+
+
+module.exports = {
+
+  SingleQuery: (ctx, next) => {
+    return randomWorldPromise().then((world) => {
+      ctx.set('Server', 'Koa');
+      ctx.type = 'application/json';
+      ctx.body = world;
+      return next();
+    })
+  },
+
+  MultipleQueries: (ctx, next) => {
+    const queries = h.getQueries(ctx);
+    const worldPromises = [];
+
+    for (let i = 0; i < queries; i++) {
+      worldPromises.push(randomWorldPromise());
+    }
+
+    return Promise.all(worldPromises).then((worlds) => {
+      ctx.set('Server', 'Koa');
+      ctx.type = 'application/json';
+      ctx.body = worlds;
+      return next();
+    });
+  },
+
+  Fortunes: (ctx, next) => {
+    return Fortunes.findAll().then((fortunes) => {
+      fortunes.push(h.additionalFortune());
+      fortunes.sort((a, b) => a.message.localeCompare(b.message));
+      
+      ctx.set('Server', 'Koa');
+      ctx.type = 'text/html';
+      return ctx.render('fortunes', { fortunes });
+    }).catch((err) => process.exit(1));
+  },
+
+  Updates: (ctx, next) => {
+    const queries = h.getQueries(ctx);
+    const worldPromises = [];
+
+    for (let i = 0; i < queries; i++) {
+      worldPromises.push(randomWorldPromise());
+    }
+
+    const worldUpdate = (world) => {
+      world.randomNumber = h.randomTfbNumber();
+
+      return Worlds.update(
+          { randomNumber: world.randomNumber },
+          { where: { id: world.id } }
+        )
+        .then((results) => world)
+        .catch((err) => process.exit(1));
+    };
+
+    return Promise
+      .all(worldPromises)
+      .map((world) => worldUpdate(world))
+      .then((updated) => {
+        ctx.set('Server', 'Koa');
+        ctx.type = 'application/json';
+        ctx.body = updated;
+        return next();
+      })
+      .catch((e) => process.exit(1));
+  }
+};

+ 29 - 0
frameworks/JavaScript/koa/helper.js

@@ -0,0 +1,29 @@
+const Handlebars = require('handlebars');
+
+const GREETING = "Hello, World";
+const HELLO_OBJ = { message: GREETING };
+
+module.exports = {
+  randomTfbNumber: () => Math.floor(Math.random() * 10000) + 1,
+
+  fillArray: (value, len) => {
+    const filled = [];
+
+    for (let i = 0; i < len; i++) {
+      filled.push(value);
+    }
+    return filled;
+  },
+
+  getQueries: (ctx) => {
+    let queries = ~~(ctx.query.queries) || 1;
+    queries = Math.min(Math.max(queries, 1), 500);
+    return queries;
+  },
+
+  additionalFortune: () => ({
+    id: 0,
+    message: 'Additional fortune added at request time.'
+  })
+
+};

+ 11 - 8
frameworks/JavaScript/koa/package.json

@@ -5,13 +5,16 @@
   "main": "app.js",
   "private": true,
   "dependencies": {
-    "co-monk": "1.0.0",
-    "koa": "1.2.5",
-    "koa-bodyparser": "2.3.0",
-    "koa-handlebars": "1.0.0",
-    "koa-mongo": "0.7.0",
-    "koa-override": "1.0.0",
-    "koa-route": "2.4.2",
-    "monk": "3.0.6"
+    "bluebird": "3.5.0",
+    "handlebars": "4.0.8",
+    "koa": "2.2.0",
+    "koa-bodyparser": "4.2.0",
+    "koa-hbs": "1.0.0-alpha.1",
+    "koa-router": "7.1.1",
+    "mongoose": "4.9.8",
+    "mysql": "2.13.0",
+    "pg": "6.1.5",
+    "pg-hstore": "2.3.2",
+    "sequelize": "3.30.4"
   }
 }

+ 2 - 5
frameworks/JavaScript/koa/setup.sh

@@ -1,9 +1,6 @@
 #!/bin/bash
 
-fw_depends mongodb nodejs
+fw_depends mongodb postgresql mysql nodejs
 
-sed -i 's|mongodb://.*/hello_world|mongodb://'"${DBHOST}"'/hello_world|g' app.js
-
-# run app
 npm install
-node --harmony app &
+node --harmony-async-await app &

+ 1 - 21
frameworks/JavaScript/koa/views/fortunes.hbs

@@ -1,21 +1 @@
-<!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>
+<!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>

+ 0 - 0
toolset/setup/linux/databases/mongodb/mongodb.sh