feat: live bandwidth tracking (RX/TX MB/s + avg response size)

- Track data_received / data_sent bytes in live metric aggregation
- Compute bwInMBps, bwOutMBps, avgRespKB per second
- Bandwidth row below stats strip: ↓ RX | ↑ TX | Avg size
- RX colour: green <20 MB/s, yellow 20-50 MB/s, red >50 MB/s
- Warning banner when RX >20 MB/s: bandwidth may cause p99 spikes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-01 20:39:16 +02:00
parent 175ba3ec7a
commit d3991416e8
4 changed files with 99 additions and 2 deletions

View File

@@ -171,12 +171,13 @@ app.post('/api/tests', (req, res) => {
// Live metric aggregation state
const live = {
durations: [], // http_req_duration values (ms)
p90durations: [], // sampled for percentile calc
durations: [],
totalReqs: 0,
failedReqs: 0,
checksTotal: 0,
checksPassed: 0,
bytesIn: 0, // data_received (bytes)
bytesOut: 0, // data_sent (bytes)
startTime: Date.now(),
lastBroadcast: 0,
};
@@ -317,6 +318,10 @@ function ingestPoint(obj, live) {
} else if (metric === 'checks') {
live.checksTotal++;
if (data.value === 1) live.checksPassed++;
} else if (metric === 'data_received') {
live.bytesIn += data.value;
} else if (metric === 'data_sent') {
live.bytesOut += data.value;
}
}
@@ -334,6 +339,12 @@ function computeLiveMetrics(live) {
const p95 = pct(95);
const score = computeScore({ p95, httpErrRate, checksRate });
const bwInMBps = +(live.bytesIn / elapsed / 1048576).toFixed(2); // MB/s
const bwOutMBps = +(live.bytesOut / elapsed / 1048576).toFixed(2);
const avgRespKB = live.totalReqs > 0
? +(live.bytesIn / live.totalReqs / 1024).toFixed(1)
: 0;
return {
totalReqs: live.totalReqs,
reqPerSec: +(live.totalReqs / elapsed).toFixed(1),
@@ -349,6 +360,9 @@ function computeLiveMetrics(live) {
p99: +pct(99).toFixed(1),
max: sorted.length ? +sorted[sorted.length - 1].toFixed(1) : 0,
score,
bwInMBps,
bwOutMBps,
avgRespKB,
};
}