Selaa lähdekoodia

Fastify - NodeJs Framework (#3955)

* Bootstrap fastify framework

* Add server header

* Fixed fastify address

* Fixed update queries

* Lock fastify dependencies
Marco Talento 7 vuotta sitten
vanhempi
commit
957f10f570

+ 1 - 0
frameworks/JavaScript/fastify/.npmrc

@@ -0,0 +1 @@
+package-lock=false

+ 51 - 0
frameworks/JavaScript/fastify/README.md

@@ -0,0 +1,51 @@
+# Fastify Benchmarking Test
+
+This is the Fastify portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+Information about Fastify can be found at https://github.com/fastify/fastify
+
+### 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
+

+ 18 - 0
frameworks/JavaScript/fastify/app.js

@@ -0,0 +1,18 @@
+const cluster = require('cluster');
+const numCPUs = require('os').cpus().length;
+
+if (cluster.isMaster) {
+  // Fork workers.
+  for (let i = 0; i < numCPUs; i++) {
+    cluster.fork();
+  }
+
+  console.log('Master starting ' + new Date().toISOString());
+
+  cluster.on('exit', () => {
+    process.exit(1);
+  });
+} else {
+  // worker task
+  require('./create-server');
+}

+ 72 - 0
frameworks/JavaScript/fastify/benchmark_config.json

@@ -0,0 +1,72 @@
+{
+  "framework": "fastify",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MongoDB",
+        "framework": "fastify",
+        "language": "JavaScript",
+        "flavor": "NodeJS",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fastify",
+        "notes": "",
+        "versus": "nodejs"
+      },
+      "mysql": {
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "fastify",
+        "language": "JavaScript",
+        "flavor": "NodeJS",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fastify",
+        "notes": "",
+        "versus": "nodejs"
+      },
+      "postgres": {
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "fastify",
+        "language": "JavaScript",
+        "flavor": "NodeJS",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "fastify",
+        "notes": "",
+        "versus": "nodejs"
+      }
+    }
+  ]
+}

+ 48 - 0
frameworks/JavaScript/fastify/create-server.js

@@ -0,0 +1,48 @@
+const fastify = require('fastify')();
+const handler = require('./handlers/handler');
+
+fastify.register(require('point-of-view'), {
+  engine: {
+    ejs: require('handlebars')
+  },
+  templates: __dirname + '/views'
+})
+
+fastify.use((req, reply, next) => {
+  reply.setHeader('Server', 'Fastify')
+
+  next()
+})
+
+fastify.get('/json', (req, reply) => {
+  reply.header('Content-Type', 'application/json')
+    .code(200)
+    .send({ message: 'Hello, World!' });
+})
+
+fastify.get('/plaintext', (req, reply) => {
+  reply.header('Content-Type', 'text/plain')
+    .code(200)
+    .send('Hello, World!');
+});
+
+const handlerName = process.env.NODE_HANDLER;
+
+if (handlerName) {
+  const dbLayer = require(`./handlers/${handlerName}`);
+
+  const routerHandler = handler(dbLayer);
+
+  fastify.get('/db', routerHandler.SingleQuery);
+  fastify.get('/queries', routerHandler.MultipleQueries);
+  fastify.get('/fortunes', routerHandler.Fortunes);
+  fastify.get('/updates', routerHandler.Updates);
+}
+
+fastify.listen(8080, '0.0.0.0', err => {
+  if (err) {
+    throw err;
+  }
+
+  console.log(`Worker started and listening on http://0.0.0.0:8080 ${new Date().toISOString()}`);
+})

+ 10 - 0
frameworks/JavaScript/fastify/fastify-mysql.dockerfile

@@ -0,0 +1,10 @@
+FROM node:10.7.0
+
+COPY ./ ./
+
+RUN npm install
+
+ENV NODE_ENV production
+ENV NODE_HANDLER sequelize
+
+CMD ["node", "app.js"]

+ 10 - 0
frameworks/JavaScript/fastify/fastify-postgres.dockerfile

@@ -0,0 +1,10 @@
+FROM node:10.7.0
+
+COPY ./ ./
+
+RUN npm install
+
+ENV NODE_ENV production
+ENV NODE_HANDLER sequelize-postgres
+
+CMD ["node", "app.js"]

+ 10 - 0
frameworks/JavaScript/fastify/fastify.dockerfile

@@ -0,0 +1,10 @@
+FROM node:10.7.0
+
+COPY ./ ./
+
+RUN npm install
+
+ENV NODE_ENV production
+ENV NODE_HANDLER mongoose
+
+CMD ["node", "app.js"]

+ 52 - 0
frameworks/JavaScript/fastify/handlers/handler.js

@@ -0,0 +1,52 @@
+const h = require('../helper');
+
+/**
+ * @param databaseLayer
+ * @returns {{SingleQuery: function(*), MultipleQueries: function(*), Fortunes: function(*), Updates: function(*)}}
+ */
+module.exports = (databaseLayer) => ({
+  SingleQuery: async (req, reply) => {
+    const world = await databaseLayer.getWorldLean(h.randomTfbNumber());
+
+    reply.send(world);
+  },
+
+  MultipleQueries: async (req, reply) => {
+    const queries = h.getQueries(req.query.queries);
+    const promisesArray = [];
+
+    for (let i = 0; i < queries; i++) {
+      promisesArray.push(databaseLayer.getWorldLean(h.randomTfbNumber()));
+    }
+
+    reply.send(await Promise.all(promisesArray));
+  },
+
+  Fortunes: async (req, reply) => {
+    const fortunes = await databaseLayer.allFortunes();
+    fortunes.push(h.additionalFortune);
+    fortunes.sort((a, b) => a.message.localeCompare(b.message));
+
+    reply.view('fortunes.hbs', { fortunes })
+  },
+
+  Updates: async (req, reply) => {
+    const queries = h.getQueries(req.query.queries);
+    const worldPromises = [];
+
+    for (let i = 0; i < queries; i++) {
+      worldPromises.push(databaseLayer.getWorld(h.randomTfbNumber()));
+    }
+
+    const worlds = await Promise.all(worldPromises);
+
+    const worldsToUpdate = worlds.map(world => {
+      world.randomNumber = h.randomTfbNumber();
+      return world
+    });
+
+    const updatedWorlds = await databaseLayer.saveWorlds(worldsToUpdate);
+
+    reply.send(updatedWorlds);
+  }
+});

+ 12 - 0
frameworks/JavaScript/fastify/handlers/mongoose.js

@@ -0,0 +1,12 @@
+const { Worlds, Fortunes } = require('../models/mongoose');
+
+module.exports = {
+  getWorld: _id => Worlds.findById(_id),
+  getWorldLean: _id => Worlds.findById(_id).select('-_id').lean(),
+  allFortunes: () => Fortunes.find({}).select('-_id'),
+  saveWorlds: async (worlds) => {
+    const updatedWorlds = await Promise.all(worlds.map(world => world.save()))
+
+    return updatedWorlds.map((w) => ({ id: w._id, randomNumber: w.randomNumber }))
+  }
+};

+ 8 - 0
frameworks/JavaScript/fastify/handlers/sequelize-postgres.js

@@ -0,0 +1,8 @@
+const {Worlds, Fortunes} = require('../models/sequelize-postgres');
+
+module.exports = {
+  getWorld: id => Worlds.findOne({where: {id}}),
+  getWorldLean: id => Worlds.findOne({where: {id}, raw: true}),
+  allFortunes: () => Fortunes.findAll(),
+  saveWorlds: worlds => Promise.all(worlds.map(world => world.save())),
+};

+ 8 - 0
frameworks/JavaScript/fastify/handlers/sequelize.js

@@ -0,0 +1,8 @@
+const {Worlds, Fortunes} = require('../models/sequelize');
+
+module.exports = {
+  getWorld: id => Worlds.findOne({where: {id}}),
+  getWorldLean: id => Worlds.findOne({where: {id}, raw: true}),
+  allFortunes: () => Fortunes.findAll(),
+  saveWorlds: worlds => Promise.all(worlds.map(world => world.save())),
+};

+ 12 - 0
frameworks/JavaScript/fastify/helper.js

@@ -0,0 +1,12 @@
+module.exports = {
+  randomTfbNumber: () => Math.floor(Math.random() * 10000) + 1,
+
+  getQueries: (queries) => {
+    return Math.min(Math.max(parseInt(queries) || 1, 1), 500);
+  },
+
+  additionalFortune: {
+    id: 0,
+    message: 'Additional fortune added at request time.'
+  }
+};

+ 30 - 0
frameworks/JavaScript/fastify/models/mongoose.js

@@ -0,0 +1,30 @@
+const Mongoose = require('mongoose');
+
+Mongoose.Promise = Promise;
+
+const { SchemaTypes } = Mongoose;
+
+Mongoose.connect('mongodb://tfb-database:27017/hello_world', { useNewUrlParser: true })
+  .catch(error => {
+    console.error(error);
+    process.exit(1);
+  });
+
+const WorldSchema = new Mongoose.Schema({
+  _id: Number,
+  randomNumber: Number,
+}, {
+    collection: 'world',
+    versionKey: false
+  });
+
+const FortuneSchema = new Mongoose.Schema({
+  id: SchemaTypes.Number,
+  message: SchemaTypes.String,
+}, {
+    collection: 'fortune',
+    versionKey: false,
+  });
+
+module.exports.Worlds = Mongoose.model('World', WorldSchema, 'world');
+module.exports.Fortunes = Mongoose.model('Fortune', FortuneSchema, 'fortune');

+ 37 - 0
frameworks/JavaScript/fastify/models/sequelize-postgres.js

@@ -0,0 +1,37 @@
+const Sequelize = require('sequelize');
+const sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+  host: 'tfb-database',
+  dialect: 'postgres',
+  logging: false
+});
+
+const { DataTypes } = Sequelize;
+const Worlds = sequelize.define('World', {
+  id: {
+    type: DataTypes.INTEGER,
+    primaryKey: true
+  },
+  randomNumber: {
+    type: DataTypes.INTEGER,
+    field: 'randomnumber'
+  },
+}, {
+    timestamps: false,
+    freezeTableName: true
+  });
+
+const Fortunes = sequelize.define('Fortune', {
+  id: {
+    type: DataTypes.INTEGER,
+    primaryKey: true
+  },
+  message: { type: DataTypes.STRING }
+}, {
+    timestamps: false,
+    freezeTableName: true
+  });
+
+module.exports = {
+  Worlds,
+  Fortunes,
+};

+ 34 - 0
frameworks/JavaScript/fastify/models/sequelize.js

@@ -0,0 +1,34 @@
+const Sequelize = require('sequelize');
+const sequelize = new Sequelize('hello_world', 'benchmarkdbuser', 'benchmarkdbpass', {
+  host: 'tfb-database',
+  dialect: 'mysql',
+  logging: false
+});
+
+const { DataTypes } = Sequelize;
+const Worlds = sequelize.define('World', {
+  id: {
+    type: DataTypes.INTEGER,
+    primaryKey: true
+  },
+  randomNumber: { type: DataTypes.INTEGER }
+}, {
+    timestamps: false,
+    freezeTableName: true
+  });
+
+const Fortunes = sequelize.define('Fortune', {
+  id: {
+    type: DataTypes.INTEGER,
+    primaryKey: true
+  },
+  message: { type: DataTypes.STRING }
+}, {
+    timestamps: false,
+    freezeTableName: true
+  });
+
+module.exports = {
+  Worlds,
+  Fortunes,
+};

+ 17 - 0
frameworks/JavaScript/fastify/package.json

@@ -0,0 +1,17 @@
+{
+  "name": "fastify-tfb",
+  "version": "0.0.1",
+  "description": "Fastify tests for TechEmpower Framework Benchmarks.",
+  "main": "app.js",
+  "private": true,
+  "dependencies": {
+    "fastify": "1.9.0",
+    "handlebars": "4.0.11",
+    "mongoose": "5.2.5",
+    "mysql2": "1.5.3",
+    "pg": "7.4.3",
+    "pg-hstore": "2.3.2",
+    "point-of-view": "1.1.0",
+    "sequelize": "4.37.10"
+  }
+}

+ 1 - 0
frameworks/JavaScript/fastify/views/fortunes.hbs

@@ -0,0 +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>