Skip to content

Commit 912fb48

Browse files
authored
Add uwebsocketsjs (#9189)
* Revert "Remove uwebsockets (#9160)" This reverts commit 4b0a91f. * Remove mysql * Don't fetch types on connect
1 parent 284b2ff commit 912fb48

File tree

10 files changed

+499
-0
lines changed

10 files changed

+499
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# uWebSockets.js Benchmarking Test
2+
3+
uWebSockets is a web server written in C/C++ (https://github.com/uNetworking/uWebSockets)
4+
5+
µWebSockets.js is a web server bypass for Node.js (https://github.com/uNetworking/uWebSockets.js)
6+
7+
## Important Libraries
8+
9+
The tests were run with:
10+
11+
- [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js/)
12+
- [postgres](https://github.com/porsager/postgres/)
13+
14+
## Database
15+
16+
There are individual handlers for each DB approach. The logic for each of them are found here:
17+
18+
- [PostgreSQL](src/database/postgres.js)
19+
20+
There are **no database endpoints** or drivers attached by default.
21+
22+
To initialize the application with one of these, run any _one_ of the following commands:
23+
24+
```sh
25+
$ DATABASE=postgres npm start
26+
```
27+
28+
## Test Endpoints
29+
30+
> Visit the test requirements [here](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview)
31+
32+
```sh
33+
$ curl localhost:8080/json
34+
$ curl localhost:8080/plaintext
35+
36+
# The following are only available with the DATABASE env var
37+
38+
$ curl localhost:8080/db
39+
$ curl localhost:8080/fortunes
40+
41+
$ curl localhost:8080/updates?queries=
42+
$ curl localhost:8080/updates?queries=2
43+
$ curl localhost:8080/updates?queries=1000
44+
$ curl localhost:8080/updates?queries=foo
45+
$ curl localhost:8080/updates?queries=0
46+
47+
$ curl localhost:8080/queries?queries=
48+
$ curl localhost:8080/queries?queries=2
49+
$ curl localhost:8080/queries?queries=1000
50+
$ curl localhost:8080/queries?queries=foo
51+
$ curl localhost:8080/queries?queries=0
52+
```
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"framework": "uwebsockets.js",
3+
"tests": [
4+
{
5+
"default": {
6+
"approach": "Realistic",
7+
"classification": "Platform",
8+
"database": "None",
9+
"database_os": "Linux",
10+
"display_name": "uWebSockets.js",
11+
"flavor": "NodeJS",
12+
"framework": "uWebSockets.js",
13+
"json_url": "/json",
14+
"language": "JavaScript",
15+
"notes": "",
16+
"orm": "Raw",
17+
"os": "Linux",
18+
"plaintext_url": "/plaintext",
19+
"platform": "nodejs",
20+
"port": 8080,
21+
"versus": "nodejs",
22+
"webserver": "None"
23+
},
24+
"postgres": {
25+
"approach": "Realistic",
26+
"classification": "Platform",
27+
"database": "Postgres",
28+
"database_os": "Linux",
29+
"db_url": "/db",
30+
"display_name": "uWebSockets.js",
31+
"flavor": "NodeJS",
32+
"fortune_url": "/fortunes",
33+
"framework": "uWebSockets.js",
34+
"language": "JavaScript",
35+
"notes": "",
36+
"orm": "Raw",
37+
"os": "Linux",
38+
"platform": "None",
39+
"port": 8080,
40+
"query_url": "/queries?queries=",
41+
"update_url": "/updates?queries=",
42+
"versus": "nodejs",
43+
"webserver": "None"
44+
}
45+
}
46+
]
47+
}

frameworks/JavaScript/uwebsockets.js/package-lock.json

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"dependencies": {
3+
"postgres": "3.4.4",
4+
"slow-json-stringify": "^2.0.1",
5+
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.44.0"
6+
},
7+
"license": "MIT",
8+
"main": "src/server.js",
9+
"name": "uwebsockets.js",
10+
"private": true,
11+
"scripts": {
12+
"dev": "node src/server.js",
13+
"start": "node src/clustered.js"
14+
},
15+
"type": "module",
16+
"version": "0.0.1"
17+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import cluster from "node:cluster";
2+
import os from "node:os";
3+
import process from "node:process";
4+
5+
if (cluster.isPrimary) {
6+
// Master Node
7+
console.log(`Primary ${process.pid} is running`);
8+
9+
// Fork workers
10+
const numCPUs = os.availableParallelism();
11+
for (let i = 0; i < numCPUs; i++) {
12+
cluster.fork();
13+
}
14+
15+
cluster.on("exit", (worker) => {
16+
console.log(`worker ${worker.process.pid} died`);
17+
process.exit(1);
18+
});
19+
} else {
20+
// Cluster Node
21+
await import("./server.js");
22+
console.log(`Worker ${process.pid} started`);
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import postgres from "postgres";
2+
3+
const sql = postgres({
4+
host: "tfb-database",
5+
user: "benchmarkdbuser",
6+
password: "benchmarkdbpass",
7+
database: "hello_world",
8+
fetch_types: false,
9+
max: 1
10+
});
11+
12+
export const fortunes = async () => await sql`SELECT id, message FROM fortune`;
13+
14+
export const find = async (id) => await sql`SELECT id, randomNumber FROM world WHERE id = ${id}`.then((arr) => arr[0]);
15+
16+
export const bulkUpdate = async (worlds) => await sql`UPDATE world SET randomNumber = (update_data.randomNumber)::int
17+
FROM (VALUES ${sql(worlds.map(world => [world.id, world.randomNumber]).sort((a, b) => (a[0] < b[0]) ? -1 : 1))}) AS update_data (id, randomNumber)
18+
WHERE world.id = (update_data.id)::int`;
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import uWebSockets from "uWebSockets.js";
2+
import {
3+
addBenchmarkHeaders,
4+
generateRandomNumber,
5+
getQueriesCount,
6+
handleError,
7+
escape,
8+
jsonSerializer,
9+
worldObjectSerializer,
10+
sortByMessage
11+
} from "./utils.js";
12+
13+
let db;
14+
const { DATABASE } = process.env;
15+
if (DATABASE) db = await import(`./database/${DATABASE}.js`);
16+
17+
const webserver = uWebSockets.App();
18+
19+
webserver.get("/plaintext", new uWebSockets.DeclarativeResponse()
20+
.writeHeader("Server", "uWebSockets.js")
21+
.writeHeader("Content-Type", "text/plain")
22+
.end("Hello, World!")
23+
);
24+
25+
webserver.get("/json", (response) => {
26+
addBenchmarkHeaders(response);
27+
response.writeHeader("Content-Type", "application/json");
28+
// response.end(JSON.stringify({ message: "Hello, World!" }));
29+
response.end(jsonSerializer({ message: "Hello, World!" }));
30+
});
31+
32+
if (db) {
33+
webserver.get("/db", async (response) => {
34+
response.onAborted(() => {
35+
response.aborted = true;
36+
});
37+
38+
try {
39+
const row = await db.find(generateRandomNumber());
40+
41+
if (response.aborted) {
42+
return;
43+
}
44+
45+
response.cork(() => {
46+
addBenchmarkHeaders(response);
47+
response.writeHeader("Content-Type", "application/json");
48+
// response.end(JSON.stringify(rows));
49+
response.end(worldObjectSerializer(row));
50+
});
51+
} catch (error) {
52+
if (response.aborted) {
53+
return;
54+
}
55+
56+
handleError(error, response);
57+
}
58+
});
59+
60+
webserver.get("/queries", async (response, request) => {
61+
response.onAborted(() => {
62+
response.aborted = true;
63+
});
64+
65+
try {
66+
const queriesCount = getQueriesCount(request);
67+
68+
const databaseJobs = new Array(queriesCount);
69+
70+
for (let i = 0; i < queriesCount; i++) {
71+
databaseJobs[i] = db.find(generateRandomNumber());
72+
}
73+
74+
const worldObjects = await Promise.all(databaseJobs);
75+
76+
if (response.aborted) {
77+
return;
78+
}
79+
80+
response.cork(() => {
81+
addBenchmarkHeaders(response);
82+
response.writeHeader("Content-Type", "application/json");
83+
response.end(JSON.stringify(worldObjects));
84+
});
85+
} catch (error) {
86+
if (response.aborted) {
87+
return;
88+
}
89+
90+
handleError(error, response);
91+
}
92+
});
93+
94+
const extra = { id: 0, message: "Additional fortune added at request time." };
95+
96+
webserver.get("/fortunes", async (response) => {
97+
response.onAborted(() => {
98+
response.aborted = true;
99+
});
100+
101+
try {
102+
const rows = [extra, ...await db.fortunes()];
103+
104+
if (response.aborted) {
105+
return;
106+
}
107+
108+
// rows.push({
109+
// id: 0,
110+
// message: "Additional fortune added at request time.",
111+
// });
112+
113+
// rows.sort((a, b) => (a.message < b.message) ? -1 : 1);
114+
sortByMessage(rows)
115+
116+
const n = rows.length
117+
118+
let html = "", i = 0;
119+
for (; i < n; i++) {
120+
html += `<tr><td>${rows[i].id}</td><td>${escape(rows[i].message)}</td></tr>`;
121+
}
122+
123+
response.cork(() => {
124+
addBenchmarkHeaders(response);
125+
response.writeHeader("Content-Type", "text/html; charset=utf-8");
126+
response.end(`<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>${html}</table></body></html>`);
127+
});
128+
} catch (error) {
129+
if (response.aborted) {
130+
return;
131+
}
132+
133+
handleError(error, response);
134+
}
135+
});
136+
137+
webserver.get("/updates", async (response, request) => {
138+
response.onAborted(() => {
139+
response.aborted = true;
140+
});
141+
142+
try {
143+
const queriesCount = getQueriesCount(request);
144+
145+
const databaseJobs = new Array(queriesCount);
146+
147+
for (let i = 0; i < queriesCount; i++) {
148+
databaseJobs[i] = db.find(generateRandomNumber());
149+
}
150+
151+
const worldObjects = await Promise.all(databaseJobs);
152+
153+
for (let i = 0; i < queriesCount; i++) {
154+
worldObjects[i].randomNumber = generateRandomNumber();
155+
}
156+
157+
await db.bulkUpdate(worldObjects);
158+
159+
if (response.aborted) {
160+
return;
161+
}
162+
163+
response.cork(() => {
164+
addBenchmarkHeaders(response);
165+
response.writeHeader("Content-Type", "application/json");
166+
response.end(JSON.stringify(worldObjects));
167+
});
168+
} catch (error) {
169+
if (response.aborted) {
170+
return;
171+
}
172+
173+
handleError(error, response);
174+
}
175+
});
176+
}
177+
178+
webserver.any("/*", (response) => {
179+
response.writeStatus("404 Not Found");
180+
addBenchmarkHeaders(response);
181+
response.writeHeader("Content-Type", "text/plain");
182+
response.end("Not Found");
183+
});
184+
185+
const host = process.env.HOST || "0.0.0.0";
186+
const port = parseInt(process.env.PORT || "8080");
187+
webserver.listen(host, port, (socket) => {
188+
if (!socket) {
189+
console.error(`Couldn't bind to http://${host}:${port}!`);
190+
process.exit(1);
191+
}
192+
193+
console.log(`Successfully bound to http://${host}:${port}.`);
194+
});

0 commit comments

Comments
 (0)