Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add loopback 3.x and 4.x #1

Merged
merged 5 commits into from
Aug 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Transpiled files
lib/loopback-next.*

# Logs
logs
*.log
Expand Down
55 changes: 38 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,44 @@
# async-frameworks
A benchmark comparing performance of async/await in different HTTP frameworks

- [email protected]
- [email protected] + [email protected]
- [email protected]
- [email protected]
- **Express** v4.16
- **Koa** v2.5 + koa-router v7.4
- **Fastify** v1.8
- **Hapi** v17.5
- **LoopBack** v3.21
- **LoopBack next**: core v0.11; repository v0.14; rest v0.19

## Results

MacBookPro Mid 2015
Processor: 2.5 GHz Intel Core i7
Memory: 16 GB 1600 MHz DDR3

### Requests per seconds

framework|rps
-|-:
hapi | 6029
fastify | 7926
koa | 7305
express | 5778
loopback | 3072
loopback-next | 2778

### Latency

_Time to handle a request in milliseconds._

framework|latency
-|-:
hapi | 1.14
fastify | 0.93
koa | 1.02
express | 1.2
loopback | 2.69
loopback-next | 3.16

Async/await is not the bottleneck!

## Usage

Expand Down Expand Up @@ -42,16 +76,3 @@ Run the benchmark.
$ npm start
```

## Results

MacBookPro Mid 2015
Processor: 2.5 GHz Intel Core i7
Memory: 16 GB 1600 MHz DDR3

Average requests per seconds:
- hapi: 6188.9
- fastify: 7800
- koa: 7334.1
- express: 6147.77

Async/await is not the bottleneck!
5 changes: 3 additions & 2 deletions bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ const autocannon = require('autocannon');
const apps = require('./lib/index');

main().then(
() => console.log('done'),
err => console.error(err));
() => { console.log('done'); process.exit(0); },
err => { console.error(err); process.exit(1); },
);

async function main() {
const reqsPerSec = {};
Expand Down
2 changes: 2 additions & 0 deletions lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const url = 'mongodb://localhost:27017/async-benchmark';

let client, products;

exports.url = url;

exports.connect = async function connect() {
client = await MongoClient.connect(url, {useNewUrlParser: true});
products = client.db().collection('products');
Expand Down
2 changes: 1 addition & 1 deletion lib/express.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ app.get('/products/:ean', (req, res, next) => {
});
});

module.exports = promisify(function start(cb) {
exports.start = promisify(function start(cb) {
app.listen(0, function() { cb(null, this.address().port); });
});

2 changes: 1 addition & 1 deletion lib/fastify.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ app.route({
}
});

module.exports = promisify(function start(cb) {
exports.start = promisify(function start(cb) {
app.listen(0, function() { cb(null, app.server.address().port); });
});

2 changes: 1 addition & 1 deletion lib/hapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ server.route({
}
});

module.exports = async function start() {
exports.start = async function start() {
await server.start();
return server.info.port;
};
Expand Down
2 changes: 2 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ module.exports = {
fastify: require('./fastify'),
koa: require('./koa'),
express: require('./express'),
loopback: require('./loopback'),
'loopback-next': require('./loopback-next'),
};
2 changes: 1 addition & 1 deletion lib/koa.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ app
.use(router.routes())
.use(router.allowedMethods());

module.exports = promisify(function start(cb) {
exports.start = promisify(function start(cb) {
app.listen(0, function() { cb(null, this.address().port); });
});

47 changes: 47 additions & 0 deletions lib/loopback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

const loopback = require('loopback');
const promisify = require('util').promisify;

const app = loopback();
app.use(loopback.rest());

exports.start = promisify(function start(cb) {
app.listen(0, function() { cb(null, this.address().port); });
});

/** Setup Product model and MongoDB connection **/

const Product = app.registry.createModel({
name: 'Product',
properties: {
_id: {type: String, id: true},
ean: {type: Number, required: true},
name: {type: String, required: true},
},
mongodb: {
collection: 'products',
},
});

app.dataSource('db', {
connector: 'mongodb',
useNewUrlParser: true,
url: require('./db').url,
});

app.model(Product, {dataSource: 'db'});

/** Setup REST API **/

Product.findByEan = function(ean) {
return this.find({where: {ean}});
};

Product.remoteMethod('findByEan', {
accepts: {arg: 'ean', type: 'number', required: true},
returns: {arg: 'result', type: Product, root: true},
http: {verb: 'get', path: '/:ean'}
});

Product.disableRemoteMethodByName('findById');
2 changes: 1 addition & 1 deletion lib/worker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const framework = process.argv[2];
const start = require(`./${framework}`);
const start = require(`./${framework}`).start;
const db = require('./db');

db.connect()
Expand Down
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
"version": "1.0.0",
"description": "A benchmark comparing performance of async/await in different HTTP frameworks",
"scripts": {
"prestart": "tsc",
"start": "node --expose-gc ./bench.js",
"test": "mocha --exit test.js"
"pretest": "tsc",
"test": "mocha"
},
"repository": {
"type": "git",
Expand All @@ -17,6 +19,9 @@
},
"homepage": "https://github.com/bajtos/async-frameworks#readme",
"dependencies": {
"@loopback/core": "^0.11.2",
"@loopback/repository": "^0.14.2",
"@loopback/rest": "^0.19.2",
"autocannon": "^2.4.1",
"byline": "^5.0.0",
"express": "^4.16.2",
Expand All @@ -25,8 +30,11 @@
"hapi": "^17.0.0-rc9",
"koa": "^2.3.0",
"koa-router": "^7.2.1",
"loopback": "^3.21.0",
"loopback-connector-mongodb": "^3.5.0",
"mocha": "^5.2.0",
"mongodb": "^3.1.1",
"supertest": "^3.0.0"
"supertest": "^3.0.0",
"typescript": "^3.0.1"
}
}
87 changes: 87 additions & 0 deletions src/loopback-next.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { get, param, RestApplication, RestBindings } from '@loopback/rest';
import { inject } from '../node_modules/@loopback/core';
import { DefaultCrudRepository, Entity, juggler, model, property, RepositoryMixin, repository } from '../node_modules/@loopback/repository';

const dbConfig = {
connector: 'mongodb',
url: require('../lib/db').url,
};

class DbDataSource extends juggler.DataSource {
static dataSourceName = 'db';

constructor(
@inject('datasources.config.db', { optional: true })
dsConfig: object = dbConfig,
) {
super(dsConfig);
}
}

@model({
settings: {
mongodb: {
collection: 'products',
},
}
})
class Product extends Entity {
@property({ id: true })
_id: string

@property({ required: true })
ean: number;

@property({ required: true })
name: string;

getId() {
return this._id;
}
}

class ProductRepository extends DefaultCrudRepository<
Product,
typeof Product.prototype._id
> {
constructor(
@inject('datasources.db') protected datasource: juggler.DataSource,
) {
super(Product, datasource);
}
}

class ProductController {
constructor(
@repository(ProductRepository)
protected repo: ProductRepository,
) { }

@get('/products/{ean}')
findByEan(
@param.path.number('ean')
ean: number
) {
return this.repo.find({where: {ean}});
}
}

class BenchApp extends RepositoryMixin(RestApplication) {
constructor() {
super({
rest: { port: 0 },
});

this.bind('datasources.config.db').to(dbConfig);
this.dataSource(DbDataSource);
this.repository(ProductRepository);
this.controller(ProductController);
}
}

const app = new BenchApp();

export async function start() {
await app.start();
return app.restServer.get(RestBindings.PORT);
}
1 change: 1 addition & 0 deletions test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--exit
6 changes: 3 additions & 3 deletions test.js → test/smoke.test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
'use strict';

const supertest = require('supertest');
const db = require('./lib/db');
const db = require('../lib/db');

const apps = require('./lib/index');
const apps = require('../lib/index');

before(db.connect);
after(db.close);

Object.keys(apps).forEach(name => {
describe(`${name} app`, () => {
it('works', async () => {
const port = await apps[name]();
const port = await apps[name].start();
await verify(port);
});
});
Expand Down
22 changes: 22 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"strictNullChecks": true,

"outDir": "lib",

"lib": ["es2018", "dom"],
"module": "commonjs",
"moduleResolution": "node",
"target": "es2017",
"sourceMap": true,
"declaration": true

},
"include": [
"src/loopback-next.ts"
]
}