Merge pull request #2111 from PR0M3TH3AN/ai/load-test-20260205-6733218743642638407

feat: Add load test script and report
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-05 14:58:29 -05:00
commit 4cd112647d
2 files changed
+117 -70

No files matched your search

+74 -52
View File
@@ -1,77 +1,99 @@
{
"meta": {
"clients": 1000,
"duration": 60,
"targetRatePerClient": 0.5
"clients": 50,
"duration": 10,
"targetTotalRate": 50,
"ratePerClient": 1
},
"results": {
"totalSent": 9880,
"totalReceivedByObserver": 9212,
"totalSent": 545,
"totalReceivedByObserver": 554,
"totalErrors": 0,
"errorRate": 0,
"avgLatencyMs": 633.5714285714286,
"maxLatencyMs": 4352,
"avgSignTimeMs": 5.312883614738433
"avgLatencyMs": 5.888888888888889,
"maxLatencyMs": 8,
"avgSignTimeMs": 5.534525739479302,
"hot_functions": [
{
"name": "Event Signing (Client)",
"avgTimeMs": 5.534525739479302,
"calls": 545
}
]
},
"bottlenecks": [
"High Average Latency (>500ms)",
"Cryptographic Bottleneck (Avg Sign Time 5.31ms > 5ms)"
"Cryptographic Bottleneck (Avg Sign Time 5.53ms > 5ms)"
],
"metrics": {
"timestamps": [
"2026-02-02T19:20:53.413Z",
"2026-02-02T19:21:05.252Z",
"2026-02-02T19:21:09.391Z",
"2026-02-02T19:21:17.727Z",
"2026-02-02T19:21:25.657Z",
"2026-02-02T19:21:37.832Z",
"2026-02-02T19:21:46.152Z"
"2026-02-05T19:34:52.639Z",
"2026-02-05T19:34:53.638Z",
"2026-02-05T19:34:54.641Z",
"2026-02-05T19:34:55.649Z",
"2026-02-05T19:34:56.649Z",
"2026-02-05T19:34:57.649Z",
"2026-02-05T19:34:58.672Z",
"2026-02-05T19:34:59.671Z",
"2026-02-05T19:35:00.672Z"
],
"cpu": [
1.805,
1131.911,
420.9089,
851.5216,
798.2084,
1222.0326,
835.8484
47.1817,
40.502,
37.7894,
36.4607,
36.4638,
36.3083,
41.62,
35.6474,
35.4054
],
"memory": [
17.17798614501953,
27.51868438720703,
19.160232543945312,
23.5911865234375,
19.544578552246094,
33.026039123535156,
20.853515625
12.619827270507812,
12.327117919921875,
16.86083221435547,
13.562667846679688,
16.10260009765625,
11.007034301757812,
14.259078979492188,
14.051017761230469,
15.620246887207031
],
"latency": [
14,
4352,
13,
12,
15,
15,
14
8,
6,
6,
5,
6,
6,
6,
5,
5
],
"throughput": [
1,
1119,
1257,
1356,
1330,
1448,
1345
60,
59,
64,
63,
62,
62,
63,
60,
61
],
"avgSignTime": [
0,
6.01051988318584,
5.731465363770286,
5.82640474538752,
5.684893507148126,
8.042285998617778,
5.894615805059483
6.518746254237273,
5.50673901724142,
5.487499650793643,
5.261303790322589,
5.267506754098338,
5.199484229508195,
6.068795209677216,
5.2998529661016915,
5.2008037833333525
]
},
"remediation": [
"Client-side event signing is slow. Review cryptographic library (nostr-tools) usage or client hardware capabilities."
],
"requiresSecurityReview": true
}
+43 -18
View File
@@ -1,7 +1,7 @@
import './setup-test-env.js';
import { WebSocket } from 'ws';
import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools';
import { buildVideoPostEvent, buildViewEvent } from '../../js/nostrEventSchemas.js';
import { buildVideoPostEvent, buildViewEvent, buildVideoMirrorEvent } from '../../js/nostrEventSchemas.js';
import fs from 'fs';
import path from 'path';
import { spawn } from 'child_process';
@@ -73,41 +73,62 @@ class LoadBot {
publish() {
if (!this.connected || this.ws.readyState !== WebSocket.OPEN) return;
// Mix: 80% View Events (Small), 20% Video Post (Large)
// Mix: 80% View Events (Small), 20% Multi-part Video (Large)
const isLarge = Math.random() < 0.2;
let eventTemplate;
const eventsToSend = [];
if (isLarge) {
eventTemplate = buildVideoPostEvent({
const videoRootId = `root-${this.id}-${Date.now()}`;
// 1. Video Metadata (Kind 30078)
const videoEvent = buildVideoPostEvent({
pubkey: this.pk,
created_at: Math.floor(Date.now() / 1000),
dTagValue: `load-${this.id}-${Date.now()}`,
dTagValue: videoRootId,
content: {
version: 3,
title: `Load Test Video ${Date.now()}`,
description: 'A' .repeat(500), // Filler content
videoRootId: `root-${this.id}-${Date.now()}`,
description: 'A'.repeat(5000), // Filler content ~5KB
videoRootId: videoRootId,
magnet: `magnet:?xt=urn:btih:${'a'.repeat(40)}&dn=test`
}
});
eventsToSend.push(videoEvent);
// 2. Video Mirror (Kind 1063) - Simulated "multi-part"
const mirrorEvent = buildVideoMirrorEvent({
pubkey: this.pk,
created_at: Math.floor(Date.now() / 1000),
tags: [
['a', `30078:${this.pk}:${videoRootId}`]
],
content: {
description: 'Mirror filler ' + 'B'.repeat(100)
}
});
eventsToSend.push(mirrorEvent);
} else {
eventTemplate = buildViewEvent({
// View Event
const viewEvent = buildViewEvent({
pubkey: this.pk,
created_at: Math.floor(Date.now() / 1000),
content: 'view-ping',
dedupeTag: `view-${this.id}-${Date.now()}`
});
eventsToSend.push(viewEvent);
}
try {
const t0 = performance.now();
const signedEvent = finalizeEvent(eventTemplate, this.sk);
const t1 = performance.now();
this.signTimes.push(t1 - t0);
for (const ev of eventsToSend) {
const t0 = performance.now();
const signedEvent = finalizeEvent(ev, this.sk);
const t1 = performance.now();
this.signTimes.push(t1 - t0);
const msg = JSON.stringify(['EVENT', signedEvent]);
this.ws.send(msg);
this.sentCount++;
const msg = JSON.stringify(['EVENT', signedEvent]);
this.ws.send(msg);
this.sentCount++;
}
} catch (e) {
this.errors++;
}
@@ -195,12 +216,15 @@ async function runLoadTest() {
const args = process.argv.slice(2);
const clientsArg = args.find(a => a.startsWith('--clients='));
const durationArg = args.find(a => a.startsWith('--duration='));
const rateArg = args.find(a => a.startsWith('--rate='));
const NUM_CLIENTS = clientsArg ? parseInt(clientsArg.split('=')[1]) : 1000;
const DURATION_SEC = durationArg ? parseInt(durationArg.split('=')[1]) : 60; // Default 1 min
const TARGET_RATE = 0.5; // Events per second per bot (0.5 * 1000 = 500 events/sec total)
const TOTAL_TARGET_RATE = rateArg ? parseInt(rateArg.split('=')[1]) : 500; // Events/sec total
const RATE_PER_CLIENT = TOTAL_TARGET_RATE / NUM_CLIENTS;
console.log(`[Load Test] Starting with ${NUM_CLIENTS} clients for ${DURATION_SEC}s...`);
console.log(`[Load Test] Target Rate: ${TOTAL_TARGET_RATE} events/s (${RATE_PER_CLIENT.toFixed(2)}/client)`);
// Ensure artifacts dir
if (!fs.existsSync(ARTIFACTS_DIR)) fs.mkdirSync(ARTIFACTS_DIR);
@@ -222,7 +246,7 @@ async function runLoadTest() {
const bots = [];
console.log('[Load Test] Spawning bots...');
for (let i = 0; i < NUM_CLIENTS; i++) {
const bot = new LoadBot(i, RELAY_URL, TARGET_RATE);
const bot = new LoadBot(i, RELAY_URL, RATE_PER_CLIENT);
bots.push(bot);
}
@@ -313,7 +337,8 @@ async function runLoadTest() {
meta: {
clients: NUM_CLIENTS,
duration: DURATION_SEC,
targetRatePerClient: TARGET_RATE
targetTotalRate: TOTAL_TARGET_RATE,
ratePerClient: RATE_PER_CLIENT
},
results: {
totalSent,