From e89aa6ae3e4a0f2c8e9cfdcbeef58cfbd0c8e86a Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Tue, 1 Sep 2026 20:51:39 +0200 Subject: [PATCH] fix(bench-243): by-region grid driven by live spec, Singapore only on prod --- benchmarks/keyed-rpc-robinhood.yml | 7 +++++++ src/components/region-grid.tsx | 29 +++++++++++++++++++++++++++-- src/lib/materialize/load.ts | 19 ++++++++++++++++--- src/lib/spec-schema.ts | 21 +++++++++++++++++++++ src/lib/spec.ts | 3 +++ src/types/benchmark.ts | 9 +++++++++ 6 files changed, 83 insertions(+), 5 deletions(-) diff --git a/benchmarks/keyed-rpc-robinhood.yml b/benchmarks/keyed-rpc-robinhood.yml index 330fba353..1cec457f3 100644 --- a/benchmarks/keyed-rpc-robinhood.yml +++ b/benchmarks/keyed-rpc-robinhood.yml @@ -43,6 +43,13 @@ findings: - "{{name:alchemy}} records {{p50:alchemy}} via its edge infrastructure." - "{{name:quicknode}} delivers {{p50:quicknode}} via its shared-endpoint fleet." +# Single-vantage bench: the by-region grid and any region-aware surface +# show only the Singapore probe. The us-east series still exists in +# Prometheus (staging exposes it via region tabs) but prod does not +# surface it. +aggregate_filters: + region: sgp + faq: - q: "Which Robinhood Chain RPC provider is fastest?" a: "Per the live data above: {{name:chainstack}} at {{p50:chainstack}} p50 (24h). It is the lowest-latency keyed provider for Robinhood Chain in this benchmark." diff --git a/src/components/region-grid.tsx b/src/components/region-grid.tsx index eeeb86091..78838d8bb 100644 --- a/src/components/region-grid.tsx +++ b/src/components/region-grid.tsx @@ -7,15 +7,40 @@ import { buildProviderColors } from "@/lib/series-colors"; type Props = { benchmark: Benchmark }; -const REGIONS = [ +const LEGACY_REGIONS = [ { key: "us-east", label: "US-East" }, { key: "eu-west", label: "EU-West" }, { key: "ap-southeast", label: "AP-Southeast" }, ] as const; +const REGION_LABELS: Record = { + "us-east": "US-East", + "us-west": "US-West", + "eu-west": "EU-West", + "ap-southeast": "AP-Southeast", + sgp: "Singapore", + global: "Global", +}; + +/** Columns are driven by the live spec, not by whatever regions the + * snapshot happens to carry (prod and staging share worker snapshots, + * so a divergent branch's regions would otherwise leak): + * 1. dimensions.region declared → those columns, in declared order. + * 2. aggregate_filters.region pinned → that single column. + * 3. neither → the legacy fixed three-column layout. */ +function regionColumns(b: Benchmark): { key: string; label: string }[] { + const dims = (b.dimensions?.region ?? []).filter((r) => r.value !== "all"); + if (dims.length > 0) return dims.map((r) => ({ key: r.value, label: r.label })); + const pinned = b.aggregateFilters?.region; + if (pinned) return [{ key: pinned, label: REGION_LABELS[pinned] ?? pinned }]; + return [...LEGACY_REGIONS]; +} + export function RegionGrid({ benchmark }: Props) { const { results, unit, extras } = benchmark; + const REGIONS = useMemo(() => regionColumns(benchmark), [benchmark]); + // Both maps recompute O(n*m) over results × regions. Memoise so the // grid doesn't reprice every cell on each parent re-render (parent // re-renders on every chain/region tab change and every chart view @@ -32,7 +57,7 @@ export function RegionGrid({ benchmark }: Props) { map.set(region.key, m); } return map; - }, [results, extras.regions]); + }, [results, extras.regions, REGIONS]); if (!results.length) return null; diff --git a/src/lib/materialize/load.ts b/src/lib/materialize/load.ts index 5b14517c5..760c93ce3 100644 --- a/src/lib/materialize/load.ts +++ b/src/lib/materialize/load.ts @@ -145,6 +145,7 @@ export function buildEditorial( findings: spec.findings, source: spec.source, dimensions: spec.dimensions, + aggregateFilters: spec.aggregate_filters, ledgerColumns: spec.ledger_columns, providerNotes: spec.provider_notes, }; @@ -164,9 +165,21 @@ export async function specToBenchmark( ): Promise { const editorial = buildEditorial(spec); - const activeLabels = activeFilterLabels(options); - const isFiltered = Object.keys(activeLabels).length > 0; - const filteredSpec = isFiltered ? applyDimensionsToSpec(spec, activeLabels) : spec; + // Merge spec-declared aggregate defaults UNDER the reader's filters: + // an explicit ?region= / tab selection always wins over the pin. The + // unfiltered-view semantics below (provider augmentation, "All" copy) + // key on the reader's filters only, so a pinned aggregate still reads + // as the bench's headline view rather than a filtered slice. + const merged: BenchmarkFilters = { + ...((spec.aggregate_filters ?? {}) as BenchmarkFilters), + ...options, + }; + const activeLabels = activeFilterLabels(merged); + const isFiltered = Object.keys(activeFilterLabels(options)).length > 0; + const filteredSpec = + Object.keys(activeLabels).length > 0 + ? applyDimensionsToSpec(spec, activeLabels) + : spec; const live = await tryLoadLive(filteredSpec, isFiltered); if (live) { diff --git a/src/lib/spec-schema.ts b/src/lib/spec-schema.ts index 63b52a504..3dea9b58d 100644 --- a/src/lib/spec-schema.ts +++ b/src/lib/spec-schema.ts @@ -382,6 +382,27 @@ export const SpecSchema = z }) .optional(), + /** + * Default dimension scope for the unfiltered ("aggregate") build. + * When set, the tier-A snapshot (sig "") is built with these labels + * injected into every query selector, so the bench's headline view, + * TL;DR, JSON-LD and OG all cite the scoped slice instead of an + * average across dimensions. A reader-applied filter (region tab, + * ?region= variant) always wins over these defaults. Presentation + * surfaces (by-region grid) also read it to restrict what a + * single-vantage bench shows. + * First use: keyed-rpc-robinhood pins region=sgp so the default view + * is the Singapore probe. */ + aggregate_filters: z + .object({ + chain: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/).optional(), + region: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/).optional(), + kind: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/).optional(), + venue: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/).optional(), + }) + .strict() + .optional(), + /** * Optional per-provider chip labels rendered next to the provider * name in the ranking row. Keys are provider slugs, values are the diff --git a/src/lib/spec.ts b/src/lib/spec.ts index fd55092d4..d1548fbe3 100644 --- a/src/lib/spec.ts +++ b/src/lib/spec.ts @@ -137,6 +137,9 @@ export function overlayEditorial(stored: Benchmark, spec: Spec): Benchmark { // worker running a divergent branch (keyed-rpc-robinhood: main is // Singapore-only while dev/worker carries the region dims). dimensions: spec.dimensions, + // Same source-of-truth rule as dimensions: the live YAML decides + // the aggregate pin, never the snapshot. + aggregateFilters: spec.aggregate_filters, // provider_notes is a YAML editorial declaration: drives the // per-provider chip rendered next to the name in the ranking row. // Overlay so newly added notes surface immediately without waiting diff --git a/src/types/benchmark.ts b/src/types/benchmark.ts index 908ae00f4..11cc3c6df 100644 --- a/src/types/benchmark.ts +++ b/src/types/benchmark.ts @@ -244,6 +244,15 @@ export type Benchmark = { kind?: { value: string; label: string }[]; venue?: { value: string; label: string }[]; }; + /** Default dimension scope for the unfiltered build (spec + * `aggregate_filters`). Presentation surfaces (e.g. the by-region + * grid) also read it to restrict what a single-vantage bench shows. */ + aggregateFilters?: { + chain?: string; + region?: string; + kind?: string; + venue?: string; + }; category: "Aggregators" | "Bridges" | "Blockchains" | "Trading" | "Wallets" | "RPCs" | "NFT APIs" | "Explorers" | "RWA"; results: ProviderResult[]; /** Per-chain leader, computed only on the unfiltered ("All chains") view