Skip to content

Commit 5b2ead2

Browse files
Added tests for postgres node module with expressjs (#8561)
1 parent 8c9c7f5 commit 5b2ead2

File tree

7 files changed

+265
-3
lines changed

7 files changed

+265
-3
lines changed

frameworks/JavaScript/express/benchmark_config.json

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
"orm": "Full",
7272
"os": "Linux",
7373
"database_os": "Linux",
74-
"display_name": "express",
74+
"display_name": "express[mysql-sequelize]",
7575
"notes": "",
7676
"versus": "nodejs"
7777
},
@@ -92,7 +92,30 @@
9292
"orm": "Full",
9393
"os": "Linux",
9494
"database_os": "Linux",
95-
"display_name": "express",
95+
"display_name": "express[postgres-sequelize]",
96+
"notes": "",
97+
"versus": "nodejs"
98+
},
99+
"postgresjs": {
100+
"json_url": "/json",
101+
"plaintext_url": "/plaintext",
102+
"db_url": "/db",
103+
"query_url": "/queries?queries=",
104+
"update_url": "/updates?queries=",
105+
"fortune_url": "/fortunes",
106+
"port": 8080,
107+
"approach": "Realistic",
108+
"classification": "Micro",
109+
"database": "Postgres",
110+
"framework": "express",
111+
"language": "JavaScript",
112+
"flavor": "NodeJS",
113+
"platform": "nodejs",
114+
"webserver": "None",
115+
"orm": "Full",
116+
"os": "Linux",
117+
"database_os": "Linux",
118+
"display_name": "express[postgres.js]",
96119
"notes": "",
97120
"versus": "nodejs"
98121
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM node:21.1.0-slim
2+
3+
COPY ./ ./
4+
5+
RUN npm install
6+
7+
ENV NODE_ENV production
8+
ENV DATABASE postgres
9+
10+
EXPOSE 8080
11+
12+
CMD ["node", "src/clustered.mjs"]

frameworks/JavaScript/express/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
"body-parser": "1.19.0",
77
"dateformat": "3.0.3",
88
"escape-html": "1.0.3",
9-
"express": "4.17.3",
9+
"express": "4.18.2",
1010
"mongoose": "5.13.20",
1111
"mysql2": "2.2.5",
1212
"pg": "8.5.0",
1313
"pg-promise": "10.7.3",
1414
"pug": "2.0.1",
15+
"postgres": "^3.4.3",
16+
"slow-json-stringify": "^2.0.1",
1517
"sequelize": "6.29.0"
1618
}
1719
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import cluster from "node:cluster";
2+
import os from "node:os";
3+
import process from "node:process";
4+
5+
process.env.DATABASE = "postgres";
6+
7+
if (cluster.isPrimary) {
8+
// Master Node
9+
console.log(`Primary ${process.pid} is running`);
10+
11+
// Fork workers
12+
const numCPUs = os.availableParallelism();
13+
for (let i = 0; i < numCPUs; i++) {
14+
cluster.fork();
15+
}
16+
17+
cluster.on("exit", (worker) => {
18+
console.log(`worker ${worker.process.pid} died`);
19+
process.exit(1);
20+
});
21+
} else {
22+
// Cluster Node
23+
await import("./server.mjs");
24+
console.log(`Worker ${process.pid} started`);
25+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
max: 1,
9+
});
10+
11+
export const fortunes = async () => await sql`SELECT id, message FROM fortune`;
12+
13+
export const find = async (id) =>
14+
await sql`SELECT id, randomNumber FROM world WHERE id = ${id}`.then(
15+
(arr) => arr[0]
16+
);
17+
18+
export const bulkUpdate = async (worlds) => {
19+
const sorted = sql(
20+
worlds
21+
.map((world) => [world.id, world.randomNumber])
22+
.sort((a, b) => (a[0] < b[0] ? -1 : 1))
23+
);
24+
await sql`UPDATE world SET randomNumber = (update_data.randomNumber)::int
25+
FROM (VALUES ${sorted}) AS update_data (id, randomNumber)
26+
WHERE world.id = (update_data.id)::int`;
27+
};
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import express from "express";
2+
import {
3+
generateRandomNumber,
4+
getQueriesCount,
5+
handleError,
6+
escape,
7+
jsonSerializer,
8+
worldObjectSerializer,
9+
sortByMessage,
10+
writeResponse,
11+
headerTypes,
12+
GREETING,
13+
} from "./utils.mjs";
14+
15+
let db;
16+
const { DATABASE } = process.env;
17+
if (DATABASE) db = await import(`./database/${DATABASE}.mjs`);
18+
19+
const extra = { id: 0, message: "Additional fortune added at request time." };
20+
21+
const app = express();
22+
23+
app.get("/plaintext", (req, res) => {
24+
writeResponse(res, GREETING, headerTypes["plain"]);
25+
});
26+
27+
app.get("/json", (req, res) => {
28+
writeResponse(res, jsonSerializer({ message: GREETING }));
29+
});
30+
31+
if (db) {
32+
app.get("/db", async (req, res) => {
33+
try {
34+
const row = await db.find(generateRandomNumber());
35+
writeResponse(res, worldObjectSerializer(row));
36+
} catch (error) {
37+
handleError(error, res);
38+
}
39+
});
40+
41+
app.get("/queries", async (req, res) => {
42+
try {
43+
const queriesCount = getQueriesCount(req);
44+
const databaseJobs = new Array(queriesCount)
45+
.fill()
46+
.map(() => db.find(generateRandomNumber()));
47+
const worldObjects = await Promise.all(databaseJobs);
48+
writeResponse(res, JSON.stringify(worldObjects));
49+
} catch (error) {
50+
handleError(error, res);
51+
}
52+
});
53+
54+
app.get("/fortunes", async (req, res) => {
55+
try {
56+
const rows = [extra, ...(await db.fortunes())];
57+
sortByMessage(rows);
58+
const n = rows.length;
59+
let html = "",
60+
i = 0;
61+
for (; i < n; i++) {
62+
html += `<tr><td>${rows[i].id}</td><td>${escape(
63+
rows[i].message
64+
)}</td></tr>`;
65+
}
66+
67+
writeResponse(
68+
res,
69+
`<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>${html}</table></body></html>`,
70+
headerTypes["html"]
71+
);
72+
} catch (error) {
73+
handleError(error, res);
74+
}
75+
});
76+
77+
app.get("/updates", async (req, res) => {
78+
try {
79+
const queriesCount = getQueriesCount(req);
80+
const databaseJobs = new Array(queriesCount);
81+
for (let i = 0; i < queriesCount; i++) {
82+
databaseJobs[i] = db.find(generateRandomNumber());
83+
}
84+
const worldObjects = await Promise.all(databaseJobs);
85+
86+
for (let i = 0; i < queriesCount; i++) {
87+
worldObjects[i].randomNumber = generateRandomNumber();
88+
}
89+
await db.bulkUpdate(worldObjects);
90+
writeResponse(res, JSON.stringify(worldObjects));
91+
} catch (error) {
92+
handleError(error, res);
93+
}
94+
});
95+
}
96+
97+
app.all("*", (req, res) => {
98+
res.status(404).send("Not Found");
99+
});
100+
101+
const host = process.env.HOST || "0.0.0.0";
102+
const port = parseInt(process.env.PORT || "8080");
103+
app.listen(port, host, () => {
104+
console.log(`Server running at http://${host}:${port}/`);
105+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { sjs, attr } from "slow-json-stringify";
2+
3+
export const GREETING = "Hello, World!";
4+
5+
export const headerTypes = {
6+
plain: "text/plain",
7+
json: "application/json",
8+
html: "text/html; charset=UTF-8",
9+
};
10+
11+
export function writeResponse(res, text, type = headerTypes["json"]) {
12+
res.writeHead(200, {
13+
"content-type": type,
14+
server: "Express",
15+
});
16+
res.end(text);
17+
}
18+
19+
export function handleError(error, response) {
20+
console.error(error);
21+
response.end("Internal Server Error");
22+
}
23+
24+
export function getQueriesCount(request) {
25+
return Math.min(parseInt(request.query["queries"]) || 1, 500);
26+
}
27+
28+
export function generateRandomNumber() {
29+
return Math.ceil(Math.random() * 10000);
30+
}
31+
32+
const escapeHTMLRules = {
33+
"&": "&#38;",
34+
"<": "&#60;",
35+
">": "&#62;",
36+
'"': "&#34;",
37+
"'": "&#39;",
38+
"/": "&#47;",
39+
};
40+
41+
const unsafeHTMLMatcher = /[&<>"'\/]/g;
42+
43+
export function escape(text) {
44+
if (unsafeHTMLMatcher.test(text) === false) return text;
45+
return text.replace(unsafeHTMLMatcher, function (m) {
46+
return escapeHTMLRules[m] || m;
47+
});
48+
}
49+
50+
export const jsonSerializer = sjs({ message: attr("string") });
51+
export const worldObjectSerializer = sjs({
52+
id: attr("number"),
53+
randomnumber: attr("number"),
54+
});
55+
56+
export function sortByMessage(arr) {
57+
const n = arr.length;
58+
for (let i = 1; i < n; i++) {
59+
const c = arr[i];
60+
let j = i - 1;
61+
while (j > -1 && c.message < arr[j].message) {
62+
arr[j + 1] = arr[j];
63+
j--;
64+
}
65+
arr[j + 1] = c;
66+
}
67+
return arr;
68+
}

0 commit comments

Comments
 (0)