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;
}
/**