verae-fleet/src/rtt.js

57 lines
1.3 KiB
JavaScript

/**
* Ring buffer of message-processing RTTs (ms) for planning percentiles.
* @module rtt
*/
export class RttWindow {
/**
* @param {number} [cap=400]
*/
constructor(cap = 400) {
this.cap = cap;
/** @type {number[]} */
this.samples = [];
}
add(ms) {
const n = Number(ms);
if (!Number.isFinite(n) || n < 0) return;
this.samples.push(n);
if (this.samples.length > this.cap) this.samples.splice(0, this.samples.length - this.cap);
}
stats() {
return summarizeRtt(this.samples);
}
}
/**
* @param {number[]} samples
*/
export function summarizeRtt(samples) {
const xs = samples.filter((n) => Number.isFinite(n) && n >= 0).slice().sort((a, b) => a - b);
if (!xs.length) {
return { count: 0, minMs: null, avgMs: null, p50Ms: null, p90Ms: null };
}
const sum = xs.reduce((a, b) => a + b, 0);
return {
count: xs.length,
minMs: round1(xs[0]),
avgMs: round1(sum / xs.length),
p50Ms: round1(percentile(xs, 0.5)),
p90Ms: round1(percentile(xs, 0.9)),
};
}
function percentile(sorted, p) {
if (sorted.length === 1) return sorted[0];
const idx = (sorted.length - 1) * p;
const lo = Math.floor(idx);
const hi = Math.ceil(idx);
if (lo === hi) return sorted[lo];
return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo);
}
function round1(n) {
return Math.round(n * 10) / 10;
}