diff --git a/report/src/pages/LoadTestDetail.tsx b/report/src/pages/LoadTestDetail.tsx index 23009a5..d910d7d 100644 --- a/report/src/pages/LoadTestDetail.tsx +++ b/report/src/pages/LoadTestDetail.tsx @@ -11,11 +11,8 @@ import { useLoadTestResult } from "../utils/useDataSeries"; import { durationToNanos, formatDuration, - formatEthFromWei, - formatGasVerbose, formatGpsVerbose, formatLoadTestTimestamp, - formatPercent, formatTps, } from "../utils/formatters"; import { @@ -23,8 +20,6 @@ import { FlashblocksLatencyStats, LatencyStats, LoadTestResult, - ObservedWindowMetrics, - TailMetrics, } from "../types"; const formatBlockRange = (range: BlockRange): string => { @@ -100,325 +95,44 @@ const SwapsPerSecondHero = ({ tps, label }: { tps: number; label: string }) => ( ); -// The full observed-window block range, matching the CLI's display: -// `first_block ..= first_block + expected_block_count - 1`. The -// `window.block_range` field is the range of blocks that actually contained -// confirmed test txs and is typically smaller — surfaced as a hint. -const formatObservedWindowRange = ( - window: ObservedWindowMetrics, -): { value: string; hint: string } | null => { - const first = window.block_range.first_block; - if (typeof first !== "number" || window.expected_block_count === 0) { - return null; - } - const end = first + window.expected_block_count - 1; - const confirmedCount = window.block_range.block_count; - return { - value: `${first.toLocaleString()} → ${end.toLocaleString()}`, - hint: `${window.expected_block_count.toLocaleString()} blocks · txs landed in ${confirmedCount.toLocaleString()}`, - }; -}; - -const OBSERVED_WINDOW_TOOLTIP = ( +const RESULTS_TOOLTIP = (

- The clean, first portion of the run, sized to the configured duration. This - mirrors what you witness watching the chain live: sustained TPS over a - period of time. Use this against OKRs like “hit 3k swaps/s on the - chain.” + Client-to-client end-to-end latency: from the moment a transaction is + submitted via eth_sendRawTransaction to when the client + receives and processes it (seeing its own txHash in a block). This mirrors + what you witness watching the chain live: sustained TPS over a period of + time. Use this against OKRs like “hit 3k swaps/s on the chain.”

); -const TAIL_INCLUSION_TOOLTIP = ( -

- Txs that landed in blocks past the observed window. Including them in the - headline would lower TPS and raise block / FB latency, but the data is - critical: it surfaces where inclusion-side optimization is still needed. -

-); - -const ObservedWindowSummary = ({ - window, -}: { - window: ObservedWindowMetrics; -}) => { - const blockRange = window.block_range; - const windowRange = formatObservedWindowRange(window); +const ResultsSummary = ({ result }: { result: LoadTestResult }) => { + const { throughput, block_range: blockRange } = result; return ( - + - - - {windowRange ? ( + + + {blockRange && ( - ) : ( - blockRange && ( - - ) )} ); }; -const TailSection = ({ - tail, - totalConfirmed, -}: { - tail: TailMetrics; - totalConfirmed: number; -}) => { - const blockRange = tail.block_range; - const hasReceiptDelay = - tail.block_receipt_delay && - durationToNanos(tail.block_receipt_delay.max) > 0; - - const timePastRows = useMemo( - () => buildLatencyRows(tail.time_past_observed_window), - [tail.time_past_observed_window], - ); - const blockLatencyRows = useMemo( - () => buildLatencyRows(tail.block_latency), - [tail.block_latency], - ); - const receiptDelayRows = useMemo( - () => (hasReceiptDelay ? buildLatencyRows(tail.block_receipt_delay) : []), - [tail.block_receipt_delay, hasReceiptDelay], - ); - const flashblocksRows = useMemo( - () => buildLatencyRows(tail.flashblocks_latency), - [tail.flashblocks_latency], - ); - - if (tail.count === 0) { - return ( - -
- No transactions landed past the observed window - {typeof tail.observed_window_end_block === "number" && ( - <> - {" "} - (boundary: block {tail.observed_window_end_block.toLocaleString()} - ) - - )} - . -
-
- ); - } - - return ( - -
- - - - {typeof tail.observed_window_end_block === "number" && ( - - )} - {blockRange && ( - - )} - - -
-

- Time past observed window -

- -
- -
-

- Block latency (tail) -

- -
- - {hasReceiptDelay && ( -
-

- Block receipt delay (tail) -

- -
- )} - -
-

- Flashblocks latency (tail) ·{" "} - {tail.flashblocks_latency.count.toLocaleString()} samples -

- -
-
-
- ); -}; - -const FullRunBaselineSection = ({ result }: { result: LoadTestResult }) => { - const submitted = result.throughput.total_submitted; - const confirmed = result.throughput.total_confirmed; - const failed = result.throughput.total_failed; - const reverted = result.throughput.total_reverted; - const blockRange = result.block_range; - - const blockLatencyRows = useMemo( - () => buildLatencyRows(result.block_latency), - [result.block_latency], - ); - const flashblocksRows = useMemo( - () => buildLatencyRows(result.flashblocks_latency), - [result.flashblocks_latency], - ); - const receiptDelayRows = useMemo( - () => - result.block_receipt_delay - ? buildLatencyRows(result.block_receipt_delay) - : [], - [result.block_receipt_delay], - ); - - return ( -
- - Full-run baseline (observed window + tail combined) - -
-

- Full-run averages dilute the clean reporting window with tail - stragglers. Use the observed-window numbers above for headline - comparisons; this section is included for completeness. -

- - - - - - - {reverted > 0 && ( - - )} - - - - - {blockRange && ( - - )} - - -
-

- Block latency (full run) -

- -
- - {result.block_receipt_delay && ( -
-

- Block receipt delay (full run) -

- -
- )} - -
-

- Flashblocks latency (full run) ·{" "} - {result.flashblocks_latency.count.toLocaleString()} samples -

- -
-
-
- ); -}; - interface LoadTestReportContentProps { result: LoadTestResult; title: string; @@ -435,34 +149,19 @@ export const LoadTestReportContent = ({ subtitle, backLink, }: LoadTestReportContentProps) => { - const observedWindow = result.observed_window; - const tail = result.tail ?? undefined; - - // Headline numbers come from observed_window when available, otherwise fall - // back to the legacy full-run fields so older S3 runs still render. - const headlineTps = observedWindow?.tps ?? result.throughput.tps; - const headlineBlockLatency = - observedWindow?.block_latency ?? result.block_latency; - const headlineFlashblocksLatency = - observedWindow?.flashblocks_latency ?? result.flashblocks_latency; - const headlineReceiptDelay = - observedWindow?.block_receipt_delay ?? result.block_receipt_delay; + const headlineTps = result.throughput.tps; + const headlineFlashblocksLatency = result.flashblocks_latency; const headlineBlockLatencyRows = useMemo( - () => buildLatencyRows(headlineBlockLatency), - [headlineBlockLatency], + () => buildLatencyRows(result.block_latency), + [result.block_latency], ); const headlineFlashblocksRows = useMemo( () => buildLatencyRows(headlineFlashblocksLatency), [headlineFlashblocksLatency], ); - const headlineReceiptDelayRows = useMemo( - () => (headlineReceiptDelay ? buildLatencyRows(headlineReceiptDelay) : []), - [headlineReceiptDelay], - ); - const headlineLabel = observedWindow ? "Observed-window TPS" : "Swaps/s"; - const latencyScopeLabel = observedWindow ? "observed window" : "full run"; + const headlineLabel = "Swaps/s"; return ( <> @@ -498,28 +197,17 @@ export const LoadTestReportContent = ({ {result.config && } - {observedWindow && } + - + - {headlineReceiptDelay && ( - - - - )} - - {tail && ( - - )} - {(() => { const reverted = result.throughput.total_reverted; @@ -562,8 +243,6 @@ export const LoadTestReportContent = ({ ); })()} - - {observedWindow && } ); }; diff --git a/report/src/types.ts b/report/src/types.ts index f3f9f64..1d9015f 100644 --- a/report/src/types.ts +++ b/report/src/types.ts @@ -130,10 +130,9 @@ export interface RustDuration { nanos: number; } +// Mirrors Rust `LatencyMetrics`. No `count` field — only +// `FlashblocksLatencyStats` carries a sample count. export interface LatencyStats { - // NOTE: `count` is currently only present on flashblocks_latency, not - // block_latency. See upgrades.md P0 #2. Treat as optional until backend fixes. - count?: number; min: RustDuration; max: RustDuration; mean: RustDuration; @@ -227,67 +226,25 @@ export interface ThroughputSample { gps: number; } -/** - * Clean reporting window for a configured-duration run. Defined as the first - * `expected_block_count` blocks starting at `block_range.first_block`. TPS/GPS - * denominator is `duration` (= expected_block_count * BLOCK_INTERVAL), not the - * full wall-clock run, so headline numbers are not diluted by tail stragglers. - * Producer added in base/base#3358. - */ -export interface ObservedWindowMetrics { - expected_block_count: number; - block_range: BlockRange; - duration: RustDuration; - confirmed_count: number; - tps: number; - gps: number; - block_latency: LatencyStats; - block_receipt_delay: LatencyStats; - flashblocks_latency: FlashblocksLatencyStats; -} - -/** - * Inclusion-delay tail: txs landing in blocks past the observed window - * (`block_number > observed_window_end_block`). `null` on continuous runs - * (no configured duration). Producer added in base/base#3358. - */ -export interface TailMetrics { - observed_window_end_block: number | null; - count: number; - confirmed_pct: number; - block_range: BlockRange; - time_past_observed_window: LatencyStats; - block_latency: LatencyStats; - block_receipt_delay: LatencyStats; - flashblocks_latency: FlashblocksLatencyStats; -} - +// Mirrors Rust `MetricsSummary` (base/base#3695). Reports client-to-client +// end-to-end latency over the full run; the earlier observed-window/tail split +// and per-tx block_receipt_delay were removed. export interface LoadTestResult { + // Present only on runs where a fatal error stopped the test. + error?: string; block_latency: LatencyStats; - // Submit-to-receipt-observation delay, full-run baseline. Optional for - // back-compat: producer added in base/base#3358. - block_receipt_delay?: LatencyStats; flashblocks_latency: FlashblocksLatencyStats; throughput: ThroughputStats; throughput_percentiles: ThroughputPercentiles; + throughput_timeseries: ThroughputSample[]; gas: GasStats; + block_range: BlockRange; // Element type is best-effort until upgrades.md P3 #7 lands. Empty arrays // dominate today, so we have no live samples to verify against. top_failure_reasons: FailureReason[]; - // Both optional for back-compat: older S3 runs predate these fields and the - // page must render without them. Sections that depend on each field are - // gated on its presence rather than rendering empty placeholders. + // Optional for back-compat: older S3 runs predate this field, and the page + // omits the config card when absent. config?: LoadTestConfig; - throughput_timeseries?: ThroughputSample[]; - // Optional for back-compat: older runs predate this field. The summary - // section gates the block range stats on its presence. - block_range?: BlockRange; - // Observed reporting window (clean TPS / latency). Optional for back-compat: - // older S3 runs predate this; the page falls back to full-run fields. - observed_window?: ObservedWindowMetrics; - // Inclusion-delay tail. `null` on continuous runs; `undefined` on older - // runs that predate the field. - tail?: TailMetrics | null; } /**