Skip to content

Commit

Permalink
feat: add ultimate express (#9390)
Browse files Browse the repository at this point in the history
* feat: add ultimate express

* feat: add ultimate express

* refactor(express): simplify response handling by removing writeResponse helper

- removed writeResponse function and headerTypes constant for direct header and response handling
- refactored routes to set headers and send responses inline, making code more declarative
- improved readability and reduced abstraction in response handling for each route

* fix: unique display names

* fix: names

* fix: add db/query/update/fortune urls

* fix: missing orm value

* fixes

* fixes

* fixes

* full refactor

* fix etag and disable x-powered-by

* generate json dynamically

* fix space

* use fjs

---------

Co-authored-by: dimden <26517362+dimdenGD@users.noreply.github.com>
  • Loading branch information
Pho3niX90 and dimdenGD authored Dec 13, 2024
1 parent f786fb5 commit f749c9f
Show file tree
Hide file tree
Showing 12 changed files with 1,175 additions and 0 deletions.
60 changes: 60 additions & 0 deletions frameworks/JavaScript/ultimate-express/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# UltimateExpress Benchmarking Test

The Ultimate Express. Fastest http server with full Express compatibility, based on [µWebSockets](https://github.com/uNetworking/uWebSockets.js).

## Important Libraries

The tests were run with:

- [ultimate-express](https://github.com/dimdenGD/ultimate-express)
- [postgres](https://github.com/porsager/postgres)
- [mariadb](https://github.com/mariadb-corporation/mariadb-connector-nodejs)
- [lru-cache](https://github.com/isaacs/node-lru-cache)

## Database

There are individual handlers for each DB approach. The logic for each of them are found here:

- [Postgres](database/postgres.js)
- [MySQL](database/mysql.js)

There are **no database endpoints** or drivers attached by default.

To initialize the application with one of these, run any _one_ of the following commands:

```sh
$ DATABASE=postgres npm start
$ DATABASE=mysql npm start
```

## Test Endpoints

> Visit the test requirements [here](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview)
```sh
$ curl localhost:8080/json
$ curl localhost:8080/plaintext

# The following are only available with the DATABASE env var

$ curl localhost:8080/db
$ curl localhost:8080/fortunes

$ curl localhost:8080/queries?queries=2
$ curl localhost:8080/queries?queries=0
$ curl localhost:8080/queries?queries=foo
$ curl localhost:8080/queries?queries=501
$ curl localhost:8080/queries?queries=

$ curl localhost:8080/updates?queries=2
$ curl localhost:8080/updates?queries=0
$ curl localhost:8080/updates?queries=foo
$ curl localhost:8080/updates?queries=501
$ curl localhost:8080/updates?queries=

$ curl localhost:8080/cached-worlds?count=2
$ curl localhost:8080/cached-worlds?count=0
$ curl localhost:8080/cached-worlds?count=foo
$ curl localhost:8080/cached-worlds?count=501
$ curl localhost:8080/cached-worlds?count=
```
157 changes: 157 additions & 0 deletions frameworks/JavaScript/ultimate-express/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import express from 'ultimate-express';
import { LRUCache } from 'lru-cache';
import cluster, { isWorker } from 'node:cluster';
import { maxQuery, maxRows } from './config.js';
import fjs from 'fast-json-stringify';

const { DATABASE } = process.env;
const db = DATABASE ? await import(`./database/${DATABASE}.js`) : null;

const jsonSerializer = fjs({
type: 'object',
properties: {
message: {
type: 'string',
format: 'unsafe',
}
}
});

const generateRandomNumber = () => Math.floor(Math.random() * maxRows) + 1;

const parseQueries = (i) => Math.min(parseInt(i) || 1, maxQuery);

const escapeHTMLRules = { '&': '&#38;', '<': '&#60;', '>': '&#62;', '"': '&#34;', "'": '&#39;', '/': '&#47;' };

const unsafeHTMLMatcher = /[&<>"'\/]/g;

const escapeHTMLCode = (text) => unsafeHTMLMatcher.test(text) ? text.replace(unsafeHTMLMatcher, function (m) { return escapeHTMLRules[m] || m; }) : text;

const cache = new LRUCache({
max: maxRows
});

const app = express();
app.set("etag", false);
app.set("x-powered-by", false);

app.get('/plaintext', (req, res) => {
res.setHeader('Server', 'UltimateExpress');
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, World!');
});

app.get('/json', (req, res) => {
res.setHeader('Server', 'UltimateExpress');
res.setHeader('Content-Type', 'application/json');
res.end(jsonSerializer({ message: "Hello, World!" }));
});

if (db) {
app.get('/db', async (req, res) => {
res.setHeader('Server', 'UltimateExpress');

try {
const world = await db.find(generateRandomNumber());
res.json(world);
} catch (error) {
throw error;
}
});

app.get('/queries', async (req, res) => {
res.setHeader('Server', 'UltimateExpress');

try {
const queries = parseQueries(req.query.queries);
const worldPromises = new Array(queries);

for (let i = 0; i < queries; i++) {
worldPromises[i] = db.find(generateRandomNumber());
}

const worlds = await Promise.all(worldPromises);

res.json(worlds);
} catch (error) {
throw error;
}
})

app.get('/updates', async (req, res) => {
res.setHeader('Server', 'UltimateExpress');

try {
const queries = parseQueries(req.query.queries);
const worldPromises = new Array(queries);

for (let i = 0; i < queries; i++) {
worldPromises[i] = db.find(generateRandomNumber());
}

const worlds = await Promise.all(worldPromises);

for (let i = 0; i < queries; i++) {
worlds[i].randomNumber = generateRandomNumber();
}

await db.bulkUpdate(worlds);

res.json(worlds);
} catch (error) {
throw error;
}
})

app.get('/fortunes', async (req, res) => {
res.setHeader('Server', 'UltimateExpress');

try {
const fortunes = await db.fortunes()

fortunes.push({ id: 0, message: 'Additional fortune added at request time.' });

fortunes.sort((a, b) => (a.message < b.message) ? -1 : 1);

const n = fortunes.length

let i = 0, html = ''
for (; i < n; i++) html += `<tr><td>${fortunes[i].id}</td><td>${escapeHTMLCode(fortunes[i].message)}</td></tr>`

res
.header('Content-Type', 'text/html; charset=utf-8')
.end(`<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>${html}</table></body></html>`);
} catch (error) {
throw error;
}
})

let isCachePopulated = false
app.get('/cached-worlds', async (req, res) => {
res.setHeader('Server', 'UltimateExpress');

try {
if (!isCachePopulated) {
const worlds = await db.getAllWorlds();
for (let i = 0; i < worlds.length; i++) {
cache.set(worlds[i].id, worlds[i]);
}
isCachePopulated = true;
}
const count = parseQueries(req.query.count);
const worlds = new Array(count);

for (let i = 0; i < count; i++) {
worlds[i] = cache.get(generateRandomNumber());
}

res.json(worlds);
} catch (error) {
throw error;
}
});
}

app.listen(8080, () => {
console.log(`${isWorker ? `${cluster.worker.id}: ` : ''}Successfully bound to http://0.0.0.0:8080`);
});
70 changes: 70 additions & 0 deletions frameworks/JavaScript/ultimate-express/benchmark_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"framework": "ultimate-express",
"tests": [
{
"default": {
"json_url": "/json",
"plaintext_url": "/plaintext",
"port": 8080,
"approach": "Realistic",
"classification": "Micro",
"database": "None",
"framework": "ultimate-express",
"language": "JavaScript",
"flavor": "None",
"orm": "Raw",
"platform": "NodeJS",
"webserver": "µws",
"os": "Linux",
"database_os": "Linux",
"display_name": "ultimate-express",
"notes": "",
"versus": "nodejs"
},
"postgres": {
"db_url": "/db",
"fortune_url": "/fortunes",
"query_url": "/queries?queries=",
"update_url": "/updates?queries=",
"cached_query_url": "/cached-worlds?count=",
"port": 8080,
"approach": "Realistic",
"classification": "Micro",
"database": "Postgres",
"framework": "ultimate-express",
"language": "JavaScript",
"flavor": "None",
"orm": "Raw",
"platform": "NodeJS",
"webserver": "µws",
"os": "Linux",
"database_os": "Linux",
"display_name": "ultimate-express-postgres",
"notes": "",
"versus": "nodejs"
},
"mysql": {
"db_url": "/db",
"fortune_url": "/fortunes",
"query_url": "/queries?queries=",
"update_url": "/updates?queries=",
"cached_query_url": "/cached-worlds?count=",
"port": 8080,
"approach": "Realistic",
"classification": "Micro",
"database": "MySQL",
"framework": "ultimate-express",
"language": "JavaScript",
"flavor": "None",
"orm": "Raw",
"platform": "NodeJS",
"webserver": "µws",
"os": "Linux",
"database_os": "Linux",
"display_name": "ultimate-express-mysql",
"notes": "",
"versus": "nodejs"
}
}
]
}
15 changes: 15 additions & 0 deletions frameworks/JavaScript/ultimate-express/clustered.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import cluster, { isPrimary, setupPrimary, fork } from 'node:cluster'
import { cpus } from 'node:os'

if (isPrimary) {
setupPrimary({
exec: 'app.js',
})
cluster.on('exit', (worker) => {
console.log(`worker ${worker.process.pid} died`)
process.exit(1)
})
for (let i = 0; i < cpus().length; i++) {
fork()
}
}
8 changes: 8 additions & 0 deletions frameworks/JavaScript/ultimate-express/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const maxQuery = 500
export const maxRows = 10000
export const clientOpts = {
host: 'tfb-database',
user: 'benchmarkdbuser',
password: 'benchmarkdbpass',
database: 'hello_world',
}
15 changes: 15 additions & 0 deletions frameworks/JavaScript/ultimate-express/database/mysql.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createPool } from 'mariadb'
import { cpus } from 'node:os'
import { clientOpts } from '../config.js'

const pool = createPool({ ...clientOpts, connectionLimit: cpus().length })

const execute = (text, values) => pool.execute(text, values || undefined)

export const fortunes = () => execute('SELECT id, message FROM fortune')

export const find = (id) => execute('SELECT id, randomNumber FROM world WHERE id = ?', [id]).then(arr => arr[0])

export const getAllWorlds = () => execute('SELECT id, randomNumber FROM world')

export const bulkUpdate = (worlds) => pool.batch('UPDATE world SET randomNumber = ? WHERE id = ?', worlds.map(world => [world.randomNumber, world.id]).sort((a, b) => (a[1] < b[1]) ? -1 : 1))
14 changes: 14 additions & 0 deletions frameworks/JavaScript/ultimate-express/database/postgres.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import postgres from 'postgres'
import { clientOpts } from '../config.js'

const sql = postgres({ ...clientOpts, max: 1 })

export const fortunes = async () => sql`SELECT id, message FROM fortune`

export const find = async (id) => sql`SELECT id, randomNumber FROM world WHERE id = ${id}`.then((arr) => arr[0])

export const getAllWorlds = async () => sql`SELECT id, randomNumber FROM world`

export const bulkUpdate = async (worlds) => await sql`UPDATE world SET randomNumber = (update_data.randomNumber)::int
FROM (VALUES ${sql(worlds.map(world => [world.id, world.randomNumber]).sort((a, b) => (a[0] < b[0]) ? -1 : 1))}) AS update_data (id, randomNumber)
WHERE world.id = (update_data.id)::int`;
Loading

0 comments on commit f749c9f

Please sign in to comment.