feat(perf): cache relay ranking in RelayHealthManager

- Implement caching for `rankRelays` to avoid redundant sorting and computations.
- Cache is invalidated on new outcomes or when quarantine status changes.
- Add `_metricsVersion` to track metrics updates.
- Benchmarks show ~3x performance improvement (0.06ms -> 0.02ms per call for 100 relays).
- Add unit tests for caching behavior and invalidation.
- Add `perf/benchmark-lock-ops.mjs`.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-18 03:33:59 +00:00
1 parent cea50da512
commit a9f30f13f9
3 files changed
+176 -18

No files matched your search

+64 -18
View File
@@ -188,6 +188,8 @@ export class RelayHealthManager {
constructor() {
this.metricsByRelay = new Map();
this.lastSnapshotAt = 0;
this._metricsVersion = 0;
this._sortedCache = null;
}
ensureMetrics(relay, config) {
@@ -203,31 +205,72 @@ export class RelayHealthManager {
lastResultAt: null,
};
this.metricsByRelay.set(relay, metrics);
this._metricsVersion += 1;
}
return metrics;
}
rankRelays(relays, config, nowMs = Date.now()) {
const summaries = relays.map((relay) => {
const metrics = this.ensureMetrics(relay, config);
const summary = summarizeRelayMetrics(metrics, nowMs);
return {
relay,
summary,
score: computeRelayScore(summary),
};
});
for (const relay of relays) {
this.ensureMetrics(relay, config);
}
return summaries.sort((a, b) => {
if (b.score !== a.score) return b.score - a.score;
if (a.summary.quarantined !== b.summary.quarantined) return a.summary.quarantined ? 1 : -1;
if (a.summary.averageLatencyMs !== b.summary.averageLatencyMs) {
if (a.summary.averageLatencyMs === null) return 1;
if (b.summary.averageLatencyMs === null) return -1;
return a.summary.averageLatencyMs - b.summary.averageLatencyMs;
if (
!this._sortedCache
|| this._sortedCache.version !== this._metricsVersion
|| nowMs >= this._sortedCache.validUntil
) {
let minQuarantineUntil = Infinity;
const entries = [];
for (const metrics of this.metricsByRelay.values()) {
const summary = summarizeRelayMetrics(metrics, nowMs);
const score = computeRelayScore(summary);
if (metrics.quarantineUntil > nowMs && metrics.quarantineUntil < minQuarantineUntil) {
minQuarantineUntil = metrics.quarantineUntil;
}
entries.push({
relay: metrics.relay,
summary,
score,
metrics,
});
}
return a.relay.localeCompare(b.relay);
});
entries.sort((a, b) => {
if (b.score !== a.score) return b.score - a.score;
if (a.summary.quarantined !== b.summary.quarantined) return a.summary.quarantined ? 1 : -1;
if (a.summary.averageLatencyMs !== b.summary.averageLatencyMs) {
if (a.summary.averageLatencyMs === null) return 1;
if (b.summary.averageLatencyMs === null) return -1;
return a.summary.averageLatencyMs - b.summary.averageLatencyMs;
}
return a.relay.localeCompare(b.relay);
});
this._sortedCache = {
version: this._metricsVersion,
validUntil: minQuarantineUntil,
entries,
};
}
const requestedSet = new Set(relays);
return this._sortedCache.entries
.filter((entry) => requestedSet.has(entry.relay))
.map((entry) => {
const quarantineRemainingMs = entry.metrics.quarantineUntil > nowMs
? entry.metrics.quarantineUntil - nowMs
: 0;
return {
relay: entry.relay,
score: entry.score,
summary: {
...entry.summary,
quarantineRemainingMs,
},
};
});
}
prioritizeRelays(relays, config, nowMs = Date.now()) {
@@ -275,6 +318,7 @@ export class RelayHealthManager {
metrics.quarantineUntil = nowMs + metrics.cooldownMs;
metrics.cooldownMs = Math.min(config.maxQuarantineCooldownMs, Math.floor(metrics.cooldownMs * 1.5));
}
this._metricsVersion += 1;
}
collectSnapshot(relays, config, nowMs = Date.now()) {
@@ -300,6 +344,8 @@ export class RelayHealthManager {
reset() {
this.metricsByRelay.clear();
this.lastSnapshotAt = 0;
this._metricsVersion = 0;
this._sortedCache = null;
}
}