4.1 KiB
PeerBandwidthTracker
(no submodules)
Purpose
PeerBandwidthTracker keeps a rolling-window record of per-peer
transfer activity for the WebRTC mesh: bytes in and out, request
counts in both directions, and an EWMA-smoothed round-trip time. It
exposes a composite score in [0, 1] that
@suaveplan/drive-webrtc's PeerManager uses to rank candidate
sources, and a canServe / assertCanServe pair the chunk-source
layer calls before serving a remote request, so noisy or
abusive peers can be detected and throttled without coupling every
caller to per-peer accounting.
Features
- Rolling window of
windowMs(default 60s) with explicitrollWindowhook callable from a periodic timer. - Per-peer byte / request counters in both directions.
- EWMA RTT (
α = 0.2) preserved across window rolls. - Composite score:
0.5 * (1 − bytesInLoad) + 0.3 * (1 − requestsInLoad) + 0.2 * rttFactor. - New peers with no samples start at
score = 1.0so they are not unfairly demoted on first sight. assertCanServethrowsDriveWebRtcError(PEER_BANDWIDTH_EXCEEDED).- Injectable
clockfor deterministic tests.
Usage: Basic Example
import { PeerBandwidthTracker } from "@suaveplan/drive-webrtc";
const tracker = new PeerBandwidthTracker();
tracker.recordBytesIn("peer-1", 16_000);
tracker.recordRtt("peer-1", 42);
const metrics = tracker.get("peer-1");
console.log(metrics.score, metrics.rttMs);
Usage: Advanced Example
import { PeerBandwidthTracker, DriveWebRtcError } from "@suaveplan/drive-webrtc";
import { FakeClock } from "@suaveplan/testing";
const clock = new FakeClock();
const tracker = new PeerBandwidthTracker({
windowMs: 1_000,
maxBytesPerWindow: 100_000,
maxRequestsPerWindow: 50,
clock: { now: () => clock.now() },
});
for (let i = 0; i < 50; i++) tracker.recordRequestIn("peer-1");
try {
tracker.assertCanServe("peer-1");
} catch (err) {
if (err instanceof DriveWebRtcError) {
// err.code === "DRIVE_WEBRTC.PEER_BANDWIDTH_EXCEEDED"
}
}
clock.advance(1_001);
tracker.rollWindow();
tracker.assertCanServe("peer-1"); // recovered
API Reference
new PeerBandwidthTracker(config?)
Construct a tracker. config accepts windowMs, maxBytesPerWindow,
maxRequestsPerWindow, clock.
recordBytesIn(peerId, bytes) / recordBytesOut(peerId, bytes)
Add bytes to the inbound or outbound counter, lazily rolling the window if the peer's window has expired.
recordRequestIn(peerId) / recordRequestOut(peerId)
Increment the inbound / outbound request counter by one.
recordRtt(peerId, rttMs)
Update the EWMA RTT estimate (α = 0.2). The first sample seeds the
estimate verbatim.
canServe(peerId): boolean
Returns false when the peer's total bytes or total requests in the
current window meet or exceed the configured thresholds.
assertCanServe(peerId): void
Throws DriveWebRtcError(PEER_BANDWIDTH_EXCEEDED) when canServe
returns false.
get(peerId): PeerMetrics
Snapshot of the peer's current window. Returns a zero-valued metrics
object with score = 1 if the peer has never been touched.
snapshot(): readonly PeerMetrics[]
Array of metrics for every tracked peer.
forget(peerId)
Drop all state for the peer — call on disconnect.
rollWindow(now?)
Force-roll every peer's window. Counters past windowMs are zeroed;
RTT estimates persist.
Implementation Notes
- The window rolls lazily on every read / record call so callers do
not need to schedule
rollWindowfor correctness — it is provided for callers that want predictable timing. - Scores compose three normalised loads with hand-picked weights (50% inbound bytes, 30% inbound requests, 20% RTT). The weights match the upstream design intent of biasing against peers that are saturating us with traffic versus peers that are slow.
- When
maxBytesPerWindow/maxRequestsPerWindowareInfinity(default), the load components collapse to zero and the score is driven entirely by RTT. - The class never throws on
record*— recording is always safe; onlyassertCanServeraises.