diff --git a/src/components/rpc-chains-leaderboard.tsx b/src/components/rpc-chains-leaderboard.tsx index bdb4e0f6..5568ce46 100644 --- a/src/components/rpc-chains-leaderboard.tsx +++ b/src/components/rpc-chains-leaderboard.tsx @@ -21,6 +21,7 @@ import type { RpcHubChain, RpcRegionKey } from "@/lib/rpc-hub-stats"; type SortKey = | "name" | "bestP50" + | "bestP90" | "us-east" | "eu-west" | "sgp" @@ -35,6 +36,7 @@ const REGION_COLS: { key: RpcRegionKey; label: string }[] = [ function sortValue(r: RpcHubChain, k: SortKey): number | string | null { if (k === "name") return r.name.toLowerCase(); if (k === "bestP50") return r.best?.p50Ms ?? null; + if (k === "bestP90") return r.bestP90Ms ?? null; if (k === "providerCount") return r.providerCount; return r.regions[k]?.p50Ms ?? null; } @@ -118,6 +120,15 @@ export function RpcChainsLeaderboard({ rows }: { rows: RpcHubChain[] }) { Best overall (3-region avg) + setSort("bestP90")} + > + + Best p90 + + {REGION_COLS.map((c) => ( - e.stopPropagation()} - > - - - {r.name} - - - +
+ e.stopPropagation()} + > + + + {r.name} + + + + e.stopPropagation()} + > + hub + +
{r.best ? ( @@ -186,6 +207,15 @@ export function RpcChainsLeaderboard({ rows }: { rows: RpcHubChain[] }) { ... )} + + {r.bestP90Ms != null ? ( + + {fmtMs(r.bestP90Ms)} + + ) : ( + - + )} + {REGION_COLS.map((c) => { const b = r.regions[c.key]; return ( @@ -215,7 +245,7 @@ export function RpcChainsLeaderboard({ rows }: { rows: RpcHubChain[] }) { mono tip={ r.unresponsiveCount - ? `${r.unresponsiveCount} cohort provider${r.unresponsiveCount > 1 ? "s" : ""} currently unresponsive on ${r.name} (probed, all calls failing). Not counted in best/fastest.` + ? `${r.unresponsiveCount} cohort provider${r.unresponsiveCount > 1 ? "s" : ""} currently unresponsive on ${r.name} (probed, all calls failing). Not counted in best/fastest. Providers: ${(r.unresponsive ?? []).map((u) => u.name).join(", ") || "n/a"}` : undefined } > @@ -243,7 +273,7 @@ export function RpcChainsLeaderboard({ rows }: { rows: RpcHubChain[] }) { {filtered.length === 0 && ( No chain matches “{q}”. diff --git a/src/lib/rpc-hub-stats.ts b/src/lib/rpc-hub-stats.ts index 350e67fe..dd520984 100644 --- a/src/lib/rpc-hub-stats.ts +++ b/src/lib/rpc-hub-stats.ts @@ -54,6 +54,7 @@ export type RpcHubProvider = { provider: string; name: string; p50Ms: number; + p90Ms?: number; p99Ms?: number; successPct?: number; sampleSize?: number; @@ -88,6 +89,8 @@ export type RpcHubChain = { * field: old blobs without it still parse. */ unresponsive?: RpcHubUnresponsiveProvider[]; best: RpcRegionBest | null; + /** p90 of the overall best provider (same row as `best`). Optional so old blobs stay parseable. */ + bestP90Ms?: number; /** Best provider per probe region. */ regions: Partial>; /** Full live provider field, sorted fastest first. */ @@ -265,11 +268,13 @@ async function buildChain(spec: Spec): Promise { } : {}), best: { provider: leader.slug, providerName: leader.name, p50Ms: round1(leader.ms.p50) }, + bestP90Ms: Number.isFinite(leader.ms.p90) && leader.ms.p90 > 0 ? round1(leader.ms.p90) : undefined, regions, providers: rows.map((r) => ({ provider: r.slug, name: r.name, p50Ms: round1(r.ms.p50), + p90Ms: Number.isFinite(r.ms.p90) && r.ms.p90 > 0 ? round1(r.ms.p90) : undefined, p99Ms: Number.isFinite(r.ms.p99) && r.ms.p99 > 0 ? round1(r.ms.p99) : undefined, successPct: Number.isFinite(r.successRate) && r.successRate > 0