|
No chain matches “{q}”.
@@ -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();
+}
diff --git a/src/lib/materialize/load.ts b/src/lib/materialize/load.ts
index 4aeb6b4d..9f211881 100644
--- a/src/lib/materialize/load.ts
+++ b/src/lib/materialize/load.ts
@@ -1014,6 +1014,51 @@ async function tryLoadLive(
});
}
+ // Archive depth augmentation for `-rpc` benches (bench cluster
+ // 044+). Reads `rpc_archive_depth_supported{chain=""}` 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();
+ 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
diff --git a/src/lib/rpc-hub-stats.ts b/src/lib/rpc-hub-stats.ts
index dd520984..e63b1b26 100644
--- a/src/lib/rpc-hub-stats.ts
+++ b/src/lib/rpc-hub-stats.ts
@@ -60,6 +60,11 @@ export type RpcHubProvider = {
sampleSize?: number;
/** p50 per probe region, keyed by region value. */
regions: Partial>;
+ /** 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 = {
@@ -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>;
/** Full live provider field, sorted fastest first. */
@@ -269,6 +278,10 @@ 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,
+ bestArchiveDepthBlocks:
+ leader.archiveDepth && leader.archiveDepth.supportedBlocks.length > 0
+ ? Math.max(...leader.archiveDepth.supportedBlocks)
+ : undefined,
regions,
providers: rows.map((r) => ({
provider: r.slug,
@@ -276,6 +289,7 @@ async function buildChain(spec: Spec): Promise {
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)
diff --git a/src/types/benchmark.ts b/src/types/benchmark.ts
index ac5ed019..b542d1a7 100644
--- a/src/types/benchmark.ts
+++ b/src/types/benchmark.ts
@@ -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.
*
|