Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/components/rpc-chains-leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type SortKey =
| "name"
| "bestP50"
| "bestP90"
| "archive"
| "us-east"
| "eu-west"
| "sgp"
Expand All @@ -37,6 +38,7 @@ 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 === "archive") return r.bestArchiveDepthBlocks ?? null;
if (k === "providerCount") return r.providerCount;
return r.regions[k]?.p50Ms ?? null;
}
Expand Down Expand Up @@ -129,6 +131,15 @@ export function RpcChainsLeaderboard({ rows }: { rows: RpcHubChain[] }) {
Best p90
</span>
</ThSort>
<ThSort
active={sortKey === "archive"}
dir={sortDir}
onClick={() => setSort("archive")}
>
<span title="Max archive block depth supported by the leader (`eth_getBalance` at head-N returns non-pruned). Higher = better for indexers / subgraphs / backfill jobs that need historical state.">
Archive
</span>
</ThSort>
{REGION_COLS.map((c) => (
<ThSort
key={c.key}
Expand Down Expand Up @@ -216,6 +227,18 @@ export function RpcChainsLeaderboard({ rows }: { rows: RpcHubChain[] }) {
<span className="text-ink-faint">-</span>
)}
</Td>
<Td
mono
tip={
r.bestArchiveDepthBlocks
? `Leader supports ${r.bestArchiveDepthBlocks.toLocaleString()} blocks of historical state. Set = {300, 7.2k, 216k, 1.3M, 5M}: 300 blocks is Geth default pruned; 5M is genesis-era full archive.`
: undefined
}
>
{r.bestArchiveDepthBlocks
? fmtArchive(r.bestArchiveDepthBlocks)
: <span className="text-ink-faint">-</span>}
</Td>
{REGION_COLS.map((c) => {
const b = r.regions[c.key];
return (
Expand Down Expand Up @@ -273,7 +296,7 @@ export function RpcChainsLeaderboard({ rows }: { rows: RpcHubChain[] }) {
{filtered.length === 0 && (
<tr>
<td
colSpan={9}
colSpan={10}
className="px-3 py-8 text-center text-[12px] text-ink-faint"
>
No chain matches &ldquo;{q}&rdquo;.
Expand Down Expand Up @@ -354,3 +377,17 @@ function fmtMs(v: number | null | undefined): string {
if (v < 1000) return `${Math.round(v)} ms`;
return `${(v / 1000).toFixed(2)} s`;
}

// Compact archive-depth display. Maps the harness's discrete depthBuckets
// to short labels: 300 blocks = "pruned" (Geth default), 7.2k blocks ≈
// 24h on 12s-block chains, 216k ≈ 30d, 1.3M ≈ 6mo, 5M ≈ 2yr. On sub-second
// chains (Arbitrum, Sei) these translate to shorter wall-clock windows;
// the raw block count in the tooltip disambiguates.
function fmtArchive(blocks: number): string {
if (blocks >= 5_000_000) return "5M+";
if (blocks >= 1_296_000) return "1.3M";
if (blocks >= 216_000) return "216k";
if (blocks >= 7200) return "7.2k";
if (blocks >= 300) return "300";
return blocks.toLocaleString();
}
45 changes: 45 additions & 0 deletions src/lib/materialize/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,51 @@ async function tryLoadLive(
});
}

// Archive depth augmentation for `-rpc` benches (bench cluster
// 044+). Reads `rpc_archive_depth_supported{chain="<slug>"}` from
// Prom (one instant query, no per-provider fan-out), aggregates
// "supported if ANY region reports 1", and attaches the ascending
// list of supported block depths to each provider row so the /rpc
// hub can render an archive column without a second Prom trip.
// Silent no-op when the query returns empty (chains where the
// archive probe is skipped: Solana / Substrate) or when the spec
// isn't a -rpc bench.
if (spec.slug.endsWith("-rpc")) {
try {
const chainForArchive = spec.slug.replace(/-rpc$/, "");
const archiveQuery = `max by(provider, depth)(rpc_archive_depth_supported{chain="${chainForArchive}"} == 1)`;
const res = await prom.query(archiveQuery);
if (res.resultType === "vector") {
const byProvider = new Map<string, number[]>();
for (const row of res.result) {
const provider = row.metric.provider;
const depthStr = row.metric.depth;
if (!provider || !depthStr) continue;
const d = Number(depthStr);
if (!Number.isFinite(d) || d <= 0) continue;
const arr = byProvider.get(provider) ?? [];
if (!arr.includes(d)) arr.push(d);
byProvider.set(provider, arr);
}
for (const r of liveResults) {
const supported = byProvider.get(r.slug);
if (supported && supported.length > 0) {
supported.sort((a, b) => a - b);
r.archiveDepth = { supportedBlocks: supported };
}
}
}
} catch (e) {
// archive is a nice-to-have; a failure here must never
// collapse the bench. Log and continue.
console.warn(
`[load/${spec.slug}] archive-depth augmentation failed: ${
e instanceof Error ? e.message : String(e)
}`,
);
}
}

// Derive lastRunAt from the actual Prom data freshness. We probe the
// first provider that has a p50 query and ask Prom for the age of
// its underlying metric. This is consistent across pages (Prom is the
Expand Down
14 changes: 14 additions & 0 deletions src/lib/rpc-hub-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export type RpcHubProvider = {
sampleSize?: number;
/** p50 per probe region, keyed by region value. */
regions: Partial<Record<RpcRegionKey, number>>;
/** Archive block depths this provider supports (subset of the
* harness's `depthBuckets` = {300, 7200, 216000, 1296000, 5000000}).
* Absent = not measured (Solana / Substrate chains skip archive)
* or not yet propagated by the worker. */
archiveDepth?: { supportedBlocks: number[] };
};

export type RpcHubUnresponsiveProvider = {
Expand Down Expand Up @@ -91,6 +96,10 @@ export type RpcHubChain = {
best: RpcRegionBest | null;
/** p90 of the overall best provider (same row as `best`). Optional so old blobs stay parseable. */
bestP90Ms?: number;
/** Max archive block depth supported by the overall best provider
* (0 or absent = archive not measured / provider Geth-pruned). Used
* by the hub as a compact single-cell 'archive support' badge. */
bestArchiveDepthBlocks?: number;
/** Best provider per probe region. */
regions: Partial<Record<RpcRegionKey, RpcRegionBest>>;
/** Full live provider field, sorted fastest first. */
Expand Down Expand Up @@ -269,13 +278,18 @@ async function buildChain(spec: Spec): Promise<RpcHubChain | null> {
: {}),
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,
bestArchiveDepthBlocks:
leader.archiveDepth && leader.archiveDepth.supportedBlocks.length > 0
? Math.max(...leader.archiveDepth.supportedBlocks)
: 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,
archiveDepth: r.archiveDepth,
successPct:
Number.isFinite(r.successRate) && r.successRate > 0
? round2(r.successRate)
Expand Down
9 changes: 9 additions & 0 deletions src/types/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ export type ProviderResult = {
successRate: number;
/** Per-provider sample count over the run window. */
sampleSize?: number;
/** RPC-cluster only: which archive depths this provider supports
* (values from the harness `depthBuckets` set: 300 / 7200 / 216000
* / 1296000 / 5000000 blocks). Populated by the loader for
* `-rpc` benches from `rpc_archive_depth_supported`. A provider
* that returns `[300]` is Geth-default-pruned; `[300, 7200,
* 216000, 1296000, 5000000]` is full archive back to genesis-era.
* Absent on non-RPC benches and on chains where the archive probe
* is skipped (Solana slot model, Substrate state model). */
archiveDepth?: { supportedBlocks: number[] };
/**
* Sample-health classification derived from sampleSize / expectedN.
*
Expand Down
Loading