Skip to content

Commit

Permalink
Merge pull request #1 from bajtos/add-loopback
Browse files Browse the repository at this point in the history
Add loopback 3.x and 4.x
  • Loading branch information
bajtos authored Aug 1, 2018
2 parents acf0d6d + a2fd724 commit e019821
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 29 deletions.
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"
]
}

0 comments on commit e019821

Please sign in to comment.