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
8 changes: 5 additions & 3 deletions app/scaling/[area]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { notFound } from 'next/navigation';

import { EmptyState } from '../../components/ui/EmptyState';
import { Text } from '../../components/ui/Text';
import { AREAS, CeilingCard } from '../components/CeilingCards';
import { AREAS } from '../components/CeilingCards';
import { RelatedRuns } from '../components/RelatedRuns';
import { RunBackedCeilingCard } from '../components/RunBackedCeilingCard';
import { SectionTable } from '../components/SectionTable';
import { Header, loadSnapshot, Unavailable } from '../loadSnapshot';

Expand All @@ -22,14 +24,14 @@ export default async function ScalingAreaPage({ params }: { params: Promise<{ ar
<Header loaded={loaded} title={`${entry[1]} · ${snapshot.network}`} crumb={entry[1]} />
<section className="flex flex-col gap-3">
<Text as="h2" variant="headline">Ceiling <span className="text-bds-gray-60">· Mgas/s</span></Text>
<CeilingCard area={entry[0]} ceilings={snapshot.ceilings} />
<RunBackedCeilingCard area={entry[0]} ceilings={snapshot.ceilings} />
</section>
{section ? (
<SectionTable section={section} windows={snapshot.windows} />
) : (
<EmptyState title="No detailed metrics yet" description="This snapshot has no metrics for this area." />
)}
<EmptyState title="Related runs" description="Benchmark and load-test runs for this area land here later." />
<RelatedRuns area={entry[0]} />
</div>
);
}
7 changes: 5 additions & 2 deletions app/scaling/components/CeilingCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import Link from 'next/link';

import { Card } from '../../components/ui/Card';
import { Text } from '../../components/ui/Text';
import type { UnifiedRun } from '../runs/lib/unified';
import { deriveBinding, type Ceiling, type CeilingInput } from '../snapshot.mjs';
import { Info } from './Info';
import { RunBackedCeilingCard } from './RunBackedCeilingCard';
import { Tag } from './Tag';

export const AREAS: [Ceiling['area'], string][] = [['da', 'DA'], ['execution', 'Execution'], ['state', 'State'], ['networking', 'Networking']];
Expand All @@ -18,13 +20,13 @@ export function CeilingCards({ ceilings }: { ceilings: Ceiling[] }) {
<section className="flex flex-col gap-3">
<Text as="h2" variant="headline">Ceilings by bottleneck <span className="text-bds-gray-60">· Mgas/s</span></Text>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
{AREAS.map(([area]) => <CeilingCard key={area} area={area} ceilings={ceilings} />)}
{AREAS.map(([area]) => <RunBackedCeilingCard key={area} area={area} ceilings={ceilings} />)}
</div>
</section>
);
}

export function CeilingCard({ area, ceilings }: { area: Ceiling['area']; ceilings: Ceiling[] }) {
export function CeilingCard({ area, ceilings, run }: { area: Ceiling['area']; ceilings: Ceiling[]; run?: UnifiedRun }) {
const name = AREAS.find(([a]) => a === area)![1];
const c = ceilings.find((x) => x.area === area)!;
return (
Expand All @@ -40,6 +42,7 @@ export function CeilingCard({ area, ceilings }: { area: Ceiling['area']; ceiling
</span>
<code className="break-words font-mono text-[12px] text-bds-gray-60">{c.formula}</code>
{c.reason ? <p className="text-bds-gray-60">{c.reason}</p> : null}
{run ? <Link className="underline" href={run.href}>Bench run {new Date(run.timestamp).toISOString().slice(0, 10)} →</Link> : null}
<ul className="flex flex-col gap-0.5 text-[12px] text-bds-gray-60 empty:hidden">
{c.inputs.map((i) => <li key={i.name}>{input(i)}</li>)}
</ul>
Expand Down
5 changes: 5 additions & 0 deletions app/scaling/components/Ladder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ export function Ladder(props: Props) {
</li>
))}
</ul>
{props.ladder.qualified.evidence?.length ? (
<Text variant="label.regular" tone="muted">
<span className="flex flex-wrap gap-x-3">Qualified sustained by {props.ladder.qualified.evidence.map((l) => <a key={l.url} className="underline" href={l.url}>{l.label}</a>)}</span>
</Text>
) : null}
<Text variant="label.regular" tone="muted">{translation(props.translations)}</Text>
</section>
);
Expand Down
2 changes: 2 additions & 0 deletions app/scaling/components/Progress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { EmptyState } from '../../components/ui/EmptyState';
import { Text } from '../../components/ui/Text';
import type { WorkItem } from '../snapshot.mjs';
import { Info } from './Info';
import { RunEvidence } from './RunEvidence';
import { Tag } from './Tag';

export function Progress({ work }: { work: WorkItem[] }) {
Expand Down Expand Up @@ -52,6 +53,7 @@ function Row({ item: w }: { item: WorkItem }) {
) : null}
<p><span className="text-bds-gray-60">Expected:</span> {w.expected ?? 'Not estimated'}</p>
{w.evidence && w.realized ? <p><span className="text-bds-gray-60">Realized:</span> {w.realized}</p> : null}
{w.evidence ? <RunEvidence url={w.evidence.url} /> : null}
<div className="flex flex-wrap gap-3">
{[w.evidence, l && { label: 'Linear', url: l.url }, ...w.links].map((link) => link ? (
<a key={link.url} className="w-fit underline" href={link.url} target="_blank" rel="noreferrer">{link.label}</a>
Expand Down
34 changes: 34 additions & 0 deletions app/scaling/components/RelatedRuns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client';

import Link from 'next/link';
import useSWR from 'swr';

import { EmptyState } from '../../components/ui/EmptyState';
import { Text } from '../../components/ui/Text';
import { RunTable } from '../runs/components/RunTable';
import { loadRuns } from '../runs/lib/runIndex';
import { weekOf } from '../runs/lib/unified';
import type { Ceiling } from '../snapshot.mjs';

const PER_KIND = 5;

export function RelatedRuns({ area }: { area: Ceiling['area'] }) {
const { data, error } = useSWR('scaling-runs', loadRuns);
const related = (data?.runs ?? []).filter((r) => (r.areas as string[]).includes(area));
// Load tests run nightly and bench runs monthly, so cap each kind or bench runs never show.
const runs = (['bench', 'load'] as const).flatMap((k) => related.filter((r) => r.kind === k).slice(0, PER_KIND)).sort((a, b) => b.timestamp - a.timestamp);
return (
<section className="flex flex-col gap-3">
<Text as="h2" variant="headline"><Link className="underline" href="/scaling/runs">Related runs →</Link></Text>
{error ? (
<EmptyState title="Runs could not be loaded" description={error instanceof Error ? error.message : String(error)} />
) : !data ? (
<Text tone="muted">Loading runs…</Text>
) : runs.length ? (
<RunTable weeks={Map.groupBy(runs, (r) => weekOf(r.timestamp))} />
) : (
<EmptyState description="No bench run or load test covers this area." />
)}
</section>
);
}
41 changes: 41 additions & 0 deletions app/scaling/components/RunBackedCeilingCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import useSWR from 'swr';

import { getDataService } from '../runs/lib/dataService';
import { getTestRunsWithStatus, type BenchmarkRuns, type BenchmarkRunWithStatus } from '../runs/lib/types';
import { toUnifiedRun } from '../runs/lib/unified';
import type { Ceiling } from '../snapshot.mjs';
import { CeilingCard } from './CeilingCards';

const gps = (r: BenchmarkRunWithStatus) => r.result?.sequencerMetrics?.gasPerSecond ?? 0;

// One benchmark run can test several configurations; its ceiling is the fastest one that passed.
function newestPassing(metadata: BenchmarkRuns): BenchmarkRunWithStatus | undefined {
const passing = getTestRunsWithStatus(metadata).filter((r) => r.status === 'success' && gps(r) > 0);
const newest = passing.toSorted((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt))[0];
return newest && passing.filter((r) => r.testConfig.BenchmarkRun === newest.testConfig.BenchmarkRun).reduce((a, b) => (gps(b) > gps(a) ? b : a));
}

// No producer records state size yet, so a bench run can only estimate the execution ceiling.
const fromRun = (c: Ceiling, run: BenchmarkRunWithStatus): Ceiling => ({
...c,
value: gps(run) / 1e6,
tag: 'estimated',
formula: 'sequencer gas/s of the newest passing bench run',
inputs: [
{ name: 'bench_run', value: `${run.testConfig.BenchmarkRun}`, unit: '', asOf: run.createdAt, status: 'observed' },
{ name: 'state_size', value: null, unit: '', asOf: null, status: 'pending' },
],
reason: 'state size not recorded',
});

export function RunBackedCeilingCard({ area, ceilings }: { area: Ceiling['area']; ceilings: Ceiling[] }) {
const { data } = useSWR('scaling-bench-metadata', () => getDataService().getMetadata());
const run = data && newestPassing(data);
const execution = ceilings.find((c) => c.area === 'execution')!;
// A measured or modeled snapshot value outranks a run-backed estimate; the snapshot is also the fallback without the API.
if (!run || execution.tag === 'measured' || execution.tag === 'modeled') return <CeilingCard area={area} ceilings={ceilings} />;
const live = ceilings.map((c) => (c === execution ? fromRun(c, run) : c));
return <CeilingCard area={area} ceilings={live} run={area === 'execution' ? toUnifiedRun(run) : undefined} />;
}
28 changes: 28 additions & 0 deletions app/scaling/components/RunEvidence.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';

import useSWR from 'swr';

import { Status } from '../runs/components/parts';
import { getDataService } from '../runs/lib/dataService';
import { getTestRunsWithStatus } from '../runs/lib/types';
import { toUnifiedRun } from '../runs/lib/unified';
import { RUN_PAGE } from '../snapshot.mjs';

export function RunEvidence({ url }: { url: string }) {
const [, id, network, timestamp] = url.match(RUN_PAGE) ?? [];
const { data: metadata } = useSWR(id ? 'scaling-bench-metadata' : null, () => getDataService().getMetadata());
const { data: result } = useSWR(network ? ['scaling-load-result', network, timestamp] : null, () => getDataService().getLoadTestResult(network, timestamp));
const runs = id
? getTestRunsWithStatus({ runs: metadata?.runs.filter((r) => `${r.testConfig.BenchmarkRun}` === id) ?? [] }).map(toUnifiedRun)
: result ? [toUnifiedRun({ network, timestamp, result })] : [];
return (
<span className="flex flex-wrap gap-x-4 text-bds-gray-60 empty:hidden">
{runs.map((r, i) => (
<span key={i} className="flex items-center gap-2">
<span className="font-mono">{r.gasPerSecond === null ? '—' : (r.gasPerSecond / 1e6).toFixed(1)} Mgas/s</span>
<Status status={r.status ?? 'unknown'} />
</span>
))}
</span>
);
}
87 changes: 87 additions & 0 deletions app/scaling/components/runBacked.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { readFileSync } from 'node:fs';

import { renderToStaticMarkup } from 'react-dom/server';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import runFixture from '../runs/fixtures/synthetic.json';
import { getTestRunsWithStatus, type BenchmarkRuns } from '../runs/lib/types';
import { toUnifiedRun, type LoadRun } from '../runs/lib/unified';
import type { Snapshot } from '../snapshot.mjs';
import { CeilingCards } from './CeilingCards';
import { Ladder } from './Ladder';
import { Progress } from './Progress';
import { RelatedRuns } from './RelatedRuns';

// Synthetic data only: real snapshots and runs are internal and never committed.
const swr = vi.hoisted(() => ({ byKey: {} as Record<string, object> }));
vi.mock('swr', () => ({ default: (key: unknown) => (key === null ? {} : swr.byKey[Array.isArray(key) ? key[0] : String(key)] ?? {}) }));

const snapshot = (): Snapshot => JSON.parse(readFileSync(new URL('../fixtures/synthetic.v2.json', import.meta.url), 'utf8'));
const metadata = runFixture.metadata as BenchmarkRuns;
const [bench] = metadata.runs;
const cards = (html: string) => html.split('rounded-2xl').slice(1);

beforeEach(() => {
swr.byKey = {};
});

describe('execution ceiling', () => {
it('shows the newest passing bench run as an estimate with its reason and a link, without making it binding', () => {
const older = { ...bench, id: 'old', createdAt: '2025-11-01T00:00:00Z', testConfig: { ...bench.testConfig, BenchmarkRun: 'test-old' }, result: { ...bench.result!, sequencerMetrics: { ...bench.result!.sequencerMetrics!, gasPerSecond: 99e6 } } };
const fasterConfig = { ...bench, outputDir: 'synthetic-1b', result: { ...bench.result!, sequencerMetrics: { ...bench.result!.sequencerMetrics!, gasPerSecond: 48e6 } } };
swr.byKey['scaling-bench-metadata'] = { data: { runs: [older, bench, fasterConfig, metadata.runs[1]] } };
const { ceilings } = snapshot();
ceilings[0].value = 70; // DA modeled above the run's 48 Mgas/s.
const [da, execution] = cards(renderToStaticMarkup(<CeilingCards ceilings={ceilings} />));
expect(execution).toMatch(/>48<\/span>.*?>Estimated</);
expect(execution).toContain('state size not recorded');
expect(execution).toContain('href="/scaling/runs/bench/test-synthetic-1"');
expect(execution).toContain('bench_run · test-synthetic-1 · observed · 2026-01-07');
expect(execution).not.toContain('>Binding<');
expect(da).toContain('>Binding<');
});

it('falls back to the snapshot when the run API is unavailable', () => {
swr.byKey['scaling-bench-metadata'] = { error: new Error('not configured') };
const [, execution] = cards(renderToStaticMarkup(<CeilingCards ceilings={snapshot().ceilings} />));
expect(execution).toMatch(/>150<\/span>.*?>Estimated</);
expect(execution).toContain('state size not recorded for the run');
expect(execution).not.toContain('/scaling/runs/bench/');
});

it('keeps a measured snapshot value over a run estimate', () => {
swr.byKey['scaling-bench-metadata'] = { data: metadata };
const { ceilings } = snapshot();
Object.assign(ceilings[1], { value: 60, tag: 'measured' });
const [, execution] = cards(renderToStaticMarkup(<CeilingCards ceilings={ceilings} />));
expect(execution).toMatch(/>60<\/span>.*?>Measured<.*?>Binding</);
});
});

it('links the qualified rung to the runs behind it', () => {
const { ladder, ceilings, translations, windows } = snapshot();
ladder.qualified = { ...ladder.qualified, value: 30, tag: 'measured', evidence: [{ label: 'Synthetic run', url: 'https://example.com/scaling/runs/bench/synthetic-1' }] };
const html = renderToStaticMarkup(<Ladder ladder={ladder} ceilings={ceilings} translations={translations} windows={windows} />);
expect(html).toContain('Qualified sustained by <a class="underline" href="https://example.com/scaling/runs/bench/synthetic-1">Synthetic run</a>');
});

it('lists the newest runs of each kind covering an area', () => {
const nightly = Array.from({ length: 6 }, (_, i) => ({ network: 'sepolia', timestamp: `2026-02-0${i + 1}T00-00-00` }));
const runs = [...getTestRunsWithStatus(metadata), ...(runFixture.loadTests as unknown as LoadRun[]), ...nightly].map(toUnifiedRun).sort((a, b) => b.timestamp - a.timestamp);
swr.byKey['scaling-runs'] = { data: { runs, loadTotal: 3 } };
const links = (area: 'execution' | 'da' | 'state') => [...renderToStaticMarkup(<RelatedRuns area={area} />).matchAll(/href="(\/scaling\/runs\/[^"]+)"/g)].map((m) => m[1]);
const newestLoads = runs.filter((r) => r.kind === 'load').slice(0, 5).map((r) => r.href);
expect(links('execution')).toEqual([...newestLoads, '/scaling/runs/bench/test-synthetic-1', '/scaling/runs/bench/test-synthetic-2']);
expect(links('da')).toEqual(newestLoads);
expect(links('state')).toEqual([]);
});

it('shows gas/s and status for evidence that points at a run page', () => {
swr.byKey['scaling-bench-metadata'] = { data: metadata };
swr.byKey['scaling-load-result'] = { data: runFixture.loadTests[0].result };
const [item] = snapshot().work;
const at = (url: string) => renderToStaticMarkup(<Progress work={[{ ...item, evidence: { label: 'Run', url } }]} />);
expect(at('https://example.com/scaling/runs/bench/test-synthetic-1')).toMatch(/42\.0 Mgas\/s.*?success/);
expect(at('https://example.com/scaling/runs/load/sepolia/2026-01-08T00-00-01')).toMatch(/12\.5 Mgas\/s.*?success/);
expect(at('https://example.com/synthetic/doc')).not.toContain('Mgas/s');
});
54 changes: 54 additions & 0 deletions app/scaling/runs/components/RunTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Link from 'next/link';

import { Card } from '../../../components/ui/Card';
import { cn } from '../../../components/ui/cn';
import { Info } from '../../components/Info';
import { Tag } from '../../components/Tag';
import { formatValue } from '../lib/formatters';
import type { RunStatus } from '../lib/types';
import type { UnifiedRun } from '../lib/unified';

const TH = 'px-3 py-2 text-left text-[12px] font-[500] text-bds-gray-60';
const TD = 'px-3 py-2 align-top';
const DOT: Record<RunStatus, string> = { success: 'bg-bds-green-50', warning: 'bg-bds-yellow-50', error: 'bg-bds-red-50', fatal: 'bg-bds-red-50', incomplete: 'bg-bds-gray-30' };

export function RunTable({ weeks }: { weeks: Map<string, UnifiedRun[]> }) {
return (
<Card className="overflow-x-auto bg-background dark:bg-white/5">
<table className="w-full border-collapse text-[13px]">
<thead>
<tr className="border-b border-bds-gray-10 dark:border-white/10">
{['When', 'Kind', 'Env', 'Profile', 'Workload', 'Mgas/s', 'Latency', 'Status', 'Duration', 'Machine'].map((h) => <th key={h} className={TH}>{h}</th>)}
</tr>
</thead>
{[...weeks].map(([week, rows]) => (
<tbody key={week}>
<tr className="bg-bds-gray-5 dark:bg-white/5"><th colSpan={10} className={TH}>Week of {week}</th></tr>
{rows.map((r) => <Row key={r.href} run={r} />)}
</tbody>
))}
</table>
</Card>
);
}

function Row({ run: r }: { run: UnifiedRun }) {
return (
<tr className="border-b border-bds-gray-10 last:border-0 dark:border-white/10">
<td className={cn(TD, 'whitespace-nowrap font-mono')}><Link className="underline" href={r.href}>{new Date(r.timestamp).toISOString().slice(0, 16).replace('T', ' ')}</Link></td>
<td className={TD}><Tag tag={r.kind} /></td>
<td className={TD}>{r.env}</td>
<td className={TD}><span className="flex items-center gap-1">{r.profile ? <Tag tag={r.profile} /> : '—'}<Info label="Why this run does not qualify">{r.qualifies.reason}</Info></span></td>
<td className={TD}>{r.workload}</td>
<td className={cn(TD, 'font-mono')}>{r.gasPerSecond === null ? '—' : (r.gasPerSecond / 1e6).toFixed(1)}</td>
<td className={cn(TD, 'whitespace-nowrap font-mono')}>
{r.latency.length ? r.latency.map((l) => <div key={l.label}>{l.label} {formatValue(l.seconds, 's')}</div>) : '—'}
</td>
<td className={cn(TD, 'whitespace-nowrap')}>
<span className="flex items-center gap-2"><span aria-hidden="true" className={cn('h-2 w-2 rounded-full', r.status ? DOT[r.status] : 'bg-bds-gray-30')} />{r.status ?? 'unknown'}</span>
</td>
<td className={cn(TD, 'font-mono')}>{r.duration === null ? '—' : formatValue(r.duration, 's')}</td>
<td className={TD}>{r.machine ?? '—'}</td>
</tr>
);
}
19 changes: 19 additions & 0 deletions app/scaling/runs/lib/runIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getDataService } from './dataService';
import { getTestRunsWithStatus } from './types';
import { toUnifiedRun } from './unified';

// report-api has no endpoint listing load-test networks.
export const NETWORK = 'sepolia';
// One request per load-test result; nightly runs grow without bound.
export const LOAD_LIMIT = 30;

export async function loadRuns() {
const api = getDataService();
const [metadata, entries] = await Promise.all([api.getMetadata(), api.getLoadTestList(NETWORK)]);
const recent = entries.slice(0, LOAD_LIMIT);
// A missing result leaves that row's metrics unknown instead of failing the whole index.
const results = await Promise.all(recent.map((e) => api.getLoadTestResult(e.network, e.timestamp).catch(() => undefined)));
const runs = [...getTestRunsWithStatus(metadata), ...recent.map((e, i) => ({ ...e, result: results[i] }))].map(toUnifiedRun);
runs.sort((a, b) => b.timestamp - a.timestamp);
return { runs, loadTotal: entries.length };
}
Loading
Loading