Skip to content

Commit 2d93ebf

Browse files
feat: startup snapshot writing card
1 parent e755f55 commit 2d93ebf

12 files changed

Lines changed: 404 additions & 276 deletions

File tree

src/api/entities.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export const tileTypeSchema = z.enum([
8585
"snapld",
8686
"snapdc",
8787
"snapin",
88+
"snapwr",
8889

8990
// shred tiles
9091
"netlnk",
@@ -338,6 +339,15 @@ export const bootProgressSchema = z.object({
338339
.nullable()
339340
.optional(),
340341
loading_full_snapshot_insert_accounts: z.number().nullable().optional(),
342+
loading_full_snapshot_snapwr_in_bytes_decompressed: z.coerce
343+
.number()
344+
.nullable()
345+
.optional(),
346+
loading_full_snapshot_snapwr_out_bytes_decompressed: z.coerce
347+
.number()
348+
.nullable()
349+
.optional(),
350+
loading_full_snapshot_snapwr_accounts: z.number().nullable().optional(),
341351

342352
loading_incremental_snapshot_elapsed_seconds: z
343353
.number()
@@ -370,6 +380,20 @@ export const bootProgressSchema = z.object({
370380
.number()
371381
.nullable()
372382
.optional(),
383+
loading_incremental_snapshot_snapwr_in_bytes_decompressed: z.coerce
384+
.number()
385+
.nullable()
386+
.optional(),
387+
loading_incremental_snapshot_snapwr_out_bytes_decompressed: z.coerce
388+
.number()
389+
.nullable()
390+
.optional(),
391+
loading_incremental_snapshot_snapwr_accounts: z
392+
.number()
393+
.nullable()
394+
.optional(),
395+
396+
accounts_database_path: z.string().nullable().optional(),
373397

374398
wait_for_supermajority_bank_hash: z.string().nullable().optional(),
375399
wait_for_supermajority_shred_version: z.string().nullable().optional(),

src/features/Overview/SlotPerformance/atoms.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export const groupedLiveIdlePerTileAtom = atom((get) => {
133133
export const snapshotTimerIndicesAtom = atom(
134134
(get): [TileType, number[]][] | undefined => {
135135
const tiles = get(tilesAtom);
136-
const tileTypes: TileType[] = ["snapld", "snapdc", "snapin"];
136+
const tileTypes: TileType[] = ["snapld", "snapdc", "snapin", "snapwr"];
137137

138138
if (!tiles) return;
139139

src/features/Overview/SlotPerformance/useTileSparkline.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ export function useScaledDataPoints({
250250
}
251251
// live
252252
else {
253+
if (stopShifting) return;
254+
253255
if (!clocks.has(tickMs)) {
254256
clocks.set(tickMs, createClock(tickMs));
255257
}
@@ -262,7 +264,15 @@ export function useScaledDataPoints({
262264
return unsub;
263265
}
264266
}
265-
}, [height, normalizedHistory, isStatic, tickMs, width, windowMs]);
267+
}, [
268+
height,
269+
normalizedHistory,
270+
isStatic,
271+
tickMs,
272+
width,
273+
windowMs,
274+
stopShifting,
275+
]);
266276

267277
return {
268278
scaledDataPoints,

src/features/StartupProgress/Firedancer/Snapshot/SnapshotBarsCard.tsx

Lines changed: 85 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,74 @@ import { Bars } from "../Bars";
55
import { useValuePerSecond } from "../useValuePerSecond";
66
import { bootProgressPhaseAtom } from "../../atoms";
77
import { useAtomValue } from "jotai";
8-
import { useEffect, useMemo } from "react";
8+
import { memo, useEffect, useMemo } from "react";
99
import StorageIcon from "@material-design-icons/svg/filled/storage.svg?react";
1010
import { compactSingleDecimalFormatter } from "../../../../numUtils";
11-
import type { FormattedBytes } from "../../../../utils";
11+
import Progress from "../../../../components/Progress";
12+
import { formatByteValue } from "./utils";
13+
14+
const gap = "4px";
1215

1316
interface SnapshotBarsCardProps {
14-
headerContent: JSX.Element;
15-
footer?: JSX.Element;
16-
throughput: number | null | undefined;
17-
containerClassName: string;
17+
title: string;
18+
progressPct: number | undefined;
19+
completed: number | null | undefined;
20+
total: number | null | undefined;
21+
barsThroughput: number | null | undefined;
1822
maxThroughput: number;
23+
headerRightContent: JSX.Element;
24+
footerText?: string | null;
1925
}
2026
export function SnapshotBarsCard({
21-
headerContent,
22-
footer,
23-
throughput,
24-
containerClassName,
27+
title,
28+
progressPct,
29+
completed,
30+
total,
31+
barsThroughput,
2532
maxThroughput,
33+
headerRightContent,
34+
footerText,
2635
}: SnapshotBarsCardProps) {
2736
return (
28-
<Card className={clsx(styles.card, styles.barsCard, containerClassName)}>
29-
<Flex
30-
justify="between"
31-
align="center"
32-
wrap="wrap"
33-
gapX="4"
34-
className={styles.cardHeader}
35-
>
36-
{headerContent}
37+
<Card className={clsx(styles.card, styles.barsCard)}>
38+
<Flex direction="column" gap="2px">
39+
<ProgressBar pct={progressPct} />
40+
<Flex
41+
justify="between"
42+
wrap="nowrap"
43+
gap={gap}
44+
className={styles.cardHeader}
45+
>
46+
<SnapshotTitle text={title} />
47+
<Flex gap={gap} minWidth="0" className={styles.headerRightSection}>
48+
<SnapshotTotalComplete completed={completed} total={total} />
49+
{headerRightContent}
50+
</Flex>
51+
</Flex>
3752
</Flex>
53+
<Bars value={barsThroughput ?? 0} max={maxThroughput} />
3854

39-
<Bars value={throughput ?? 0} max={maxThroughput} />
40-
41-
{footer}
55+
<MSnapshotPath path={footerText} />
4256
</Card>
4357
);
4458
}
4559

60+
interface ProgressBarProps {
61+
pct: number | undefined;
62+
}
63+
function ProgressBar({ pct }: ProgressBarProps) {
64+
const bootPhase = useAtomValue(bootProgressPhaseAtom);
65+
// jump to inital value on phase change
66+
return (
67+
<Flex key={bootPhase} gap="10px" align="center">
68+
<Progress className={styles.progressBar} value={pct ?? 0} />
69+
<Text className={clsx(styles.secondaryColor, styles.progressPctText)}>
70+
{pct == null ? "-" : pct.toFixed(0)}%
71+
</Text>
72+
</Flex>
73+
);
74+
}
75+
4676
function ValueUnitText({
4777
value,
4878
unit,
@@ -53,27 +83,26 @@ function ValueUnitText({
5383
return (
5484
<>
5585
<Text>{value ?? "--"}</Text>
56-
{unit && (
57-
<>
58-
{" "}
59-
<Text className={styles.secondaryColor}>{unit}</Text>
60-
</>
61-
)}
86+
{unit && <Text className={styles.secondaryColor}> {unit}</Text>}
6287
</>
6388
);
6489
}
6590

6691
interface SnapshotTitleProps {
6792
text: string;
6893
}
69-
export function SnapshotTitle({ text }: SnapshotTitleProps) {
70-
return <Text className={clsx(styles.title, styles.ellipsis)}>{text}</Text>;
94+
function SnapshotTitle({ text }: SnapshotTitleProps) {
95+
return <Text className={clsx(styles.leftColumn, styles.title)}>{text}</Text>;
7196
}
7297

7398
interface AccountsRateProps {
99+
isComplete: boolean;
74100
cumulativeAccounts?: number | null;
75101
}
76-
export function AccountsRate({ cumulativeAccounts }: AccountsRateProps) {
102+
export function AccountsRate({
103+
isComplete,
104+
cumulativeAccounts,
105+
}: AccountsRateProps) {
77106
const phase = useAtomValue(bootProgressPhaseAtom);
78107

79108
const { valuePerSecond: accountsPerSecond, reset } = useValuePerSecond(
@@ -87,68 +116,75 @@ export function AccountsRate({ cumulativeAccounts }: AccountsRateProps) {
87116
}, [phase, reset]);
88117

89118
const value = useMemo(() => {
119+
if (isComplete) return 0;
90120
// Possible to have no rate due to only having a single point
91121
if (cumulativeAccounts != null && accountsPerSecond == null) return "0";
92122
if (accountsPerSecond == null) return;
93123
return compactSingleDecimalFormatter.format(accountsPerSecond);
94-
}, [accountsPerSecond, cumulativeAccounts]);
124+
}, [accountsPerSecond, cumulativeAccounts, isComplete]);
95125

96126
return (
97-
<div className={styles.accountsRate}>
127+
<div className={styles.rightColumn}>
98128
<ValueUnitText value={value} unit="Accounts / sec" />
99129
</div>
100130
);
101131
}
102132

103133
interface SnapshotTotalCompleteProps {
104-
completed: FormattedBytes | undefined;
105-
total: FormattedBytes | undefined;
134+
completed: number | null | undefined;
135+
total: number | null | undefined;
106136
}
107-
export function SnapshotTotalComplete({
137+
function SnapshotTotalComplete({
108138
completed,
109139
total,
110140
}: SnapshotTotalCompleteProps) {
141+
const completedObj = formatByteValue(completed);
142+
const totalObj = formatByteValue(total);
143+
111144
return (
112-
<div className={styles.total}>
113-
<ValueUnitText value={completed?.value} unit={completed?.unit} />
145+
<Flex className={styles.centerColumn}>
146+
<ValueUnitText value={completedObj?.value} unit={completedObj?.unit} />
114147

115148
<Text> / </Text>
116149

117-
<ValueUnitText value={total?.value} unit={total?.unit} />
118-
</div>
150+
<ValueUnitText value={totalObj?.value} unit={totalObj?.unit} />
151+
</Flex>
119152
);
120153
}
121154

122155
interface SnapshotThroughputProps {
123156
prefix?: string;
124-
throughput: FormattedBytes | undefined;
157+
throughput: number | null | undefined;
125158
}
126159
export function SnapshotThroughput({
127160
prefix,
128161
throughput,
129162
}: SnapshotThroughputProps) {
163+
const throughputObj = formatByteValue(throughput);
164+
130165
return (
131-
<div className={clsx(styles.throughput, { [styles.withPrefix]: !!prefix })}>
166+
<div className={styles.rightColumn}>
132167
{prefix && <Text className={styles.secondaryColor}>{prefix} </Text>}
133-
<ValueUnitText value={throughput?.value} unit={throughput?.unit} />
168+
<ValueUnitText value={throughputObj?.value} unit={throughputObj?.unit} />
134169
<Text className={styles.secondaryColor}>/sec</Text>
135170
</div>
136171
);
137172
}
138173

139-
interface SnapshotReadPathProps {
140-
readPath?: string | null;
174+
interface SnapshotPathProps {
175+
path?: string | null;
141176
}
142-
export function SnapshotReadPath({ readPath }: SnapshotReadPathProps) {
177+
const MSnapshotPath = memo(function SnapshotPath({ path }: SnapshotPathProps) {
178+
if (!path) return;
143179
return (
144180
<Flex
145181
align="center"
146182
gap="10px"
147183
wrap="nowrap"
148-
className={styles.readPathContainer}
184+
className={styles.pathContainer}
149185
>
150186
<StorageIcon />
151-
<Text className={clsx(styles.readPath, styles.ellipsis)}>{readPath}</Text>
187+
<Text className={clsx(styles.path, styles.ellipsis)}>{path}</Text>
152188
</Flex>
153189
);
154-
}
190+
});

0 commit comments

Comments
 (0)