Skip to content

Commit 104ec88

Browse files
authored
Merge pull request #84 from MatthewWid/session-benchmarks
Benchmark suite for pushing events to individual clients
2 parents ac06b03 + 87dbc35 commit 104ec88

File tree

7 files changed

+171
-37
lines changed

7 files changed

+171
-37
lines changed

examples/benchmarks/benchmark.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {suite as suiteChannelPushManySessions} from "./suites/channel-push-many-sessions";
1+
import {suite as suiteChannelBroadcast} from "./suites/channel-broadcast";
2+
import {suite as suiteSessionPush} from "./suites/session-push";
23

3-
Promise.all([suiteChannelPushManySessions.setup()]).then((suites) => {
4+
Promise.all([suiteSessionPush.setup()]).then((suites) => {
45
for (const suite of suites) {
56
suite.run();
67
}

examples/benchmarks/lib/createClientPool.ts

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
1-
import EventSource from "eventsource";
1+
import type {EventSource} from "eventsource";
2+
import {createEventSource} from "./createEventSource";
23

3-
export interface ClientPoolOptions {
4-
port: number;
5-
numberOfClients?: number;
6-
}
4+
export type CleanupClientPool = () => void;
75

8-
export const createClientPool = async ({
9-
port,
10-
numberOfClients = 1,
11-
}: ClientPoolOptions): Promise<() => void> => {
12-
const sources = new Set<EventSource>();
13-
const listeners = new Set<Promise<unknown>>();
6+
export const createClientPool = async (
7+
port: number,
8+
numberOfClients = 1
9+
): Promise<CleanupClientPool> => {
10+
const listeners = new Set<Promise<EventSource>>();
1411

1512
for (let index = 0; index < numberOfClients; ++index) {
16-
const eventsource = new EventSource(`http://localhost:${port}`);
17-
const listener = new Promise((resolve) =>
18-
eventsource.addEventListener("open", resolve)
19-
);
20-
21-
sources.add(eventsource);
22-
listeners.add(listener);
13+
listeners.add(createEventSource(port));
2314
}
2415

25-
await Promise.all(listeners);
16+
const sources = await Promise.all(listeners);
2617

2718
return () => {
2819
for (const eventsource of sources) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {EventSource} from "eventsource";
2+
3+
const createEventSource = (port: number, path = "/sse") => {
4+
const url = `http://localhost:${port}${path}`;
5+
const eventSource = new EventSource(url);
6+
7+
return new Promise<EventSource>((resolve, reject) => {
8+
eventSource.addEventListener("open", () => resolve(eventSource));
9+
eventSource.addEventListener("error", (error) => reject(error.message));
10+
});
11+
};
12+
13+
export {createEventSource};

examples/benchmarks/suites/channel-push-many-sessions.ts renamed to examples/benchmarks/suites/channel-broadcast.ts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/* eslint-disable @typescript-eslint/ban-ts-comment */
2-
31
import {createChannel, createSession} from "better-sse";
42
// @ts-ignore
53
import EasySse from "easy-server-sent-events";
@@ -8,25 +6,25 @@ import SseChannel from "sse-channel";
86
import {createClientPool} from "../lib/createClientPool";
97
import {Suite} from "./Suite";
108

11-
export const suite = new Suite("Push events with channels", async () => {
9+
export const suite = new Suite("Broadcast events with channels", async () => {
1210
const numberOfClients = 10;
1311

1412
await suite.addBenchmark("better-sse", async (server, port, listen) => {
15-
let count = 0;
16-
1713
const channel = createChannel();
1814

19-
server.get("/", async (req, res) => {
15+
server.get("/sse", async (req, res) => {
2016
channel.register(await createSession(req, res));
2117
});
2218

2319
await listen();
2420

21+
let count = 0;
22+
2523
return {
2624
run: () => {
2725
channel.broadcast(++count);
2826
},
29-
teardown: await createClientPool({port, numberOfClients}),
27+
teardown: await createClientPool(port, numberOfClients),
3028
};
3129
});
3230

@@ -35,7 +33,7 @@ export const suite = new Suite("Push events with channels", async () => {
3533

3634
const channel = new SseChannel({jsonEncode: true});
3735

38-
server.get("/", (req, res) => {
36+
server.get("/sse", (req, res) => {
3937
channel.addClient(req, res);
4038
});
4139

@@ -51,29 +49,29 @@ export const suite = new Suite("Push events with channels", async () => {
5149
id: count,
5250
});
5351
},
54-
teardown: await createClientPool({port, numberOfClients}),
52+
teardown: await createClientPool(port, numberOfClients),
5553
};
5654
});
5755

5856
await suite.addBenchmark(
5957
"easy-server-sent-events",
6058
async (server, port, listen) => {
61-
let count = 0;
62-
63-
const {SSE, send} = EasySse({endpoint: "/"});
59+
const {SSE, send} = EasySse({endpoint: "/sse"});
6460

65-
server.get("/", (req, res, next) => {
61+
server.get("/sse", (req, res, next) => {
6662
SSE(req, res, next);
6763
res.flushHeaders();
6864
});
6965

7066
await listen();
7167

68+
let count = 0;
69+
7270
return {
7371
run: () => {
7472
send("all", "message", ++count);
7573
},
76-
teardown: await createClientPool({port, numberOfClients}),
74+
teardown: await createClientPool(port, numberOfClients),
7775
};
7876
}
7977
);
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import {createServer as createRawHttpServer} from "node:http";
2+
import {type Session, createSession} from "better-sse";
3+
import type {Response as ExpressResponse} from "express";
4+
import SSE, {type Client} from "sse";
5+
// @ts-ignore
6+
import SseChannel from "sse-channel";
7+
import {createEventSource} from "../lib/createEventSource";
8+
import {Suite} from "./Suite";
9+
10+
export const suite = new Suite("Push events with sessions", async () => {
11+
await suite.addBenchmark("better-sse", async (server, port, listen) => {
12+
let session: Session;
13+
14+
server.get("/sse", async (req, res) => {
15+
session = await createSession(req, res);
16+
});
17+
18+
await listen();
19+
20+
const eventSource = await createEventSource(port);
21+
22+
let count = 0;
23+
24+
return {
25+
run: () => {
26+
session.push(++count);
27+
},
28+
teardown: () => eventSource.close(),
29+
};
30+
});
31+
32+
await suite.addBenchmark("sse-channel", async (server, port, listen) => {
33+
const channel = new SseChannel({jsonEncode: true});
34+
35+
let res: ExpressResponse;
36+
37+
server.get("/sse", (req, _res) => {
38+
res = _res;
39+
channel.addClient(req, res);
40+
});
41+
42+
await listen();
43+
44+
const eventSource = await createEventSource(port);
45+
46+
let count = 0;
47+
48+
return {
49+
run: () => {
50+
++count;
51+
52+
channel.send(
53+
{
54+
event: "message",
55+
data: count,
56+
id: count,
57+
},
58+
[res]
59+
);
60+
},
61+
teardown: () => eventSource.close(),
62+
};
63+
});
64+
65+
await suite.addBenchmark("sse", async (server) => {
66+
const port = ++Suite.port;
67+
68+
// `sse` package cannot attach to an Express instance directly, so wrap with a raw Node HTTP server
69+
const wrapper = createRawHttpServer(server);
70+
71+
const sse = new SSE(wrapper);
72+
73+
let client: Client;
74+
75+
sse.on("connection", (_client) => {
76+
client = _client;
77+
});
78+
79+
await new Promise<void>((resolve) => wrapper.listen(port, () => resolve()));
80+
81+
const eventSource = await createEventSource(port);
82+
83+
let count = 0;
84+
85+
return {
86+
run: () => {
87+
const stringified = (count++).toString();
88+
89+
client.send({
90+
event: "message",
91+
data: stringified,
92+
id: stringified,
93+
});
94+
},
95+
teardown: () => eventSource.close(),
96+
};
97+
});
98+
});

examples/package-lock.json

Lines changed: 32 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"express": "^4.21.1",
1616
"node-os-utils": "^1.3.7",
1717
"pem": "^1.14.8",
18+
"sse": "^0.0.8",
1819
"sse-channel": "^4.0.0",
1920
"ts-node": "^10.9.2",
2021
"ts-node-dev": "^2.0.0"
@@ -24,6 +25,7 @@
2425
"@types/express": "^5.0.0",
2526
"@types/node": "^20.16.12",
2627
"@types/node-os-utils": "^1.3.4",
27-
"@types/pem": "^1.14.4"
28+
"@types/pem": "^1.14.4",
29+
"@types/sse": "^0.0.0"
2830
}
2931
}

0 commit comments

Comments
 (0)