Skip to content

Commit 45a9e28

Browse files
feat: remaining seconds for startup phases
1 parent bf1f99f commit 45a9e28

23 files changed

+375
-327
lines changed

src/atoms.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,8 @@ export const peersAtom = atomWithImmer<Record<string, Peer>>({});
426426

427427
export const peersListAtom = atom((get) => Object.values(get(peersAtom)));
428428

429+
export const peersCountAtom = atom((get) => get(peersListAtom).length);
430+
429431
export const peersAtomFamily = atomFamily((peer?: string) =>
430432
atom((get) => (peer !== undefined ? get(peersAtom)[peer] : undefined)),
431433
);

src/features/StartupProgress/Firedancer/Body.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import {
66
isStartupProgressExpandedAtom,
77
showStartupProgressAtom,
88
} from "../atoms";
9-
import { Flex } from "@radix-ui/themes";
9+
import { Box, Flex } from "@radix-ui/themes";
1010
import clsx from "clsx";
11-
import { Header } from "./Header";
11+
import { Header } from "./PhaseHeader/Header";
1212
import { BootPhaseEnum } from "../../../api/entities";
1313
import { bootProgressContainerElAtom } from "../../../atoms";
1414
import Gossip from "./Gossip";
@@ -56,29 +56,30 @@ function BootProgressContent({ phase }: BootProgressContentProps) {
5656
const isNarrow = useMedia("(max-width: 750px)");
5757

5858
return (
59-
<Flex
59+
<Box
6060
ref={(el: HTMLDivElement) => setBootProgressContainerEl(el)}
61-
direction="column"
6261
overflowY="auto"
6362
className={clsx(styles.container, phaseClass, {
6463
[styles.collapsed]: !showStartupProgress || !isStartupProgressExpanded,
6564
})}
6665
>
6766
<Flex
6867
direction="column"
68+
height="100%"
6969
width="100%"
7070
maxWidth={appMaxWidth}
7171
mx="auto"
7272
px={isNarrow ? "20px" : "89px"}
73-
pb="20px"
7473
>
7574
<Header />
7675

7776
{phase === BootPhaseEnum.joining_gossip && <Gossip />}
7877
{(phase === BootPhaseEnum.loading_full_snapshot ||
7978
phase === BootPhaseEnum.loading_incremental_snapshot) && <Snapshot />}
8079
{phase === BootPhaseEnum.catching_up && <CatchingUp />}
80+
81+
<Box pb="20px" />
8182
</Flex>
82-
</Flex>
83+
</Box>
8384
);
8485
}

src/features/StartupProgress/Firedancer/CatchingUp/BarsStats.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,18 @@ import { useAtomValue } from "jotai";
33
import styles from "./catchingUp.module.css";
44
import { catchingUpStartSlotAtom, latestTurbineSlotAtom } from "./atoms";
55
import { completedSlotAtom } from "../../../../api/atoms";
6+
import type { CatchingUpRates } from "./useCatchingUpRates";
67

78
interface CatchingUpBarsProps {
8-
catchingUpRatesRef: React.MutableRefObject<{
9-
totalSlotsEstimate?: number;
10-
replaySlotsPerSecond?: number;
11-
turbineSlotsPerSecond?: number;
12-
}>;
9+
catchingUpRates: CatchingUpRates;
1310
}
14-
export function BarsStats({ catchingUpRatesRef }: CatchingUpBarsProps) {
11+
export function BarsStats({ catchingUpRates }: CatchingUpBarsProps) {
1512
const startSlot = useAtomValue(catchingUpStartSlotAtom);
1613
const latestTurbineSlot = useAtomValue(latestTurbineSlotAtom);
1714
const latestReplaySlot = useAtomValue(completedSlotAtom);
1815

19-
const replayRate = catchingUpRatesRef.current.replaySlotsPerSecond;
20-
const turbineHeadRate = catchingUpRatesRef.current.turbineSlotsPerSecond;
16+
const replayRate = catchingUpRates.replaySlotsPerSecond;
17+
const turbineHeadRate = catchingUpRates.turbineSlotsPerSecond;
2118
const catchUpRate =
2219
replayRate == null || turbineHeadRate == null
2320
? undefined

src/features/StartupProgress/Firedancer/CatchingUp/CatchingUpBars.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,12 @@ import { Box } from "@radix-ui/themes";
1414
import { useThrottledCallback } from "use-debounce";
1515
import { completedSlotAtom } from "../../../../api/atoms";
1616
import { useMeasure } from "react-use";
17+
import type { CatchingUpRates } from "./useCatchingUpRates";
1718

1819
const emptyChartData: uPlot.AlignedData = [[0], [null]];
1920

2021
interface CatchingUpBarsProps {
21-
catchingUpRatesRef: React.MutableRefObject<{
22-
totalSlotsEstimate?: number;
23-
replaySlotsPerSecond?: number;
24-
turbineSlotsPerSecond?: number;
25-
}>;
22+
catchingUpRatesRef: React.MutableRefObject<CatchingUpRates>;
2623
}
2724
export function CatchingUpBars({ catchingUpRatesRef }: CatchingUpBarsProps) {
2825
const [measureRef, measureRect] = useMeasure<HTMLDivElement>();

src/features/StartupProgress/Firedancer/CatchingUp/catchingUp.module.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
padding: 14px;
66
gap: 14px;
77
border: 1px solid rgba(255, 255, 255, 0.1);
8-
background: rgba(234, 103, 103, 0.05);
8+
border-radius: 8px;
9+
background: rgba(250, 250, 250, 0.05);
910
color: var(--boot-progress-primary-text-color);
1011
}
1112

src/features/StartupProgress/Firedancer/CatchingUp/index.tsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,74 @@
1-
import { Box, Card, Flex, Text } from "@radix-ui/themes";
1+
import { Box, Flex, Text } from "@radix-ui/themes";
22
import { CatchingUpBars } from "./CatchingUpBars";
33
import { BarsFooter } from "./BarsFooter";
44
import BarsLabels from "./BarsLabels";
55
import { useAtomValue, useSetAtom } from "jotai";
6-
import { catchingUpContainerElAtom, hasCatchingUpDataAtom } from "./atoms";
6+
import {
7+
catchingUpContainerElAtom,
8+
catchingUpStartSlotAtom,
9+
hasCatchingUpDataAtom,
10+
latestTurbineSlotAtom,
11+
} from "./atoms";
712
import ShredsChart from "../../../Overview/ShredsProgression/ShredsChart";
813
import styles from "./catchingUp.module.css";
914
import CatchingUpTiles from "./CatchingUpTiles";
10-
import { PhaseHeader } from "../PhaseHeader";
15+
import PhaseHeader from "../PhaseHeader";
1116
import useEstimateTotalSlots from "./useCatchingUpRates";
1217
import { BarsStats } from "./BarsStats";
1318
import { ShredsChartLegend } from "../../../Overview/ShredsProgression/ShredsChartLegend";
19+
import { completedSlotAtom } from "../../../../api/atoms";
20+
import { useMemo } from "react";
1421

1522
export default function CatchingUp() {
1623
const setContainerEl = useSetAtom(catchingUpContainerElAtom);
1724
const hasCatchingUpData = useAtomValue(hasCatchingUpDataAtom);
1825
const catchingUpRatesRef = useEstimateTotalSlots();
1926

27+
const startSlot = useAtomValue(catchingUpStartSlotAtom);
28+
const latestTurbineSlot = useAtomValue(latestTurbineSlotAtom);
29+
const latestReplaySlot = useAtomValue(completedSlotAtom);
30+
31+
const phaseCompletePct = useMemo(() => {
32+
if (
33+
startSlot == null ||
34+
latestTurbineSlot == null ||
35+
latestReplaySlot == null
36+
) {
37+
return 0;
38+
}
39+
40+
const totalSlotsToReplay = latestTurbineSlot - startSlot + 1;
41+
if (!totalSlotsToReplay) return 0;
42+
43+
const replayedSlots = latestReplaySlot - startSlot + 1;
44+
return (100 * replayedSlots) / totalSlotsToReplay;
45+
}, [latestReplaySlot, latestTurbineSlot, startSlot]);
2046
return (
2147
<>
22-
<PhaseHeader phase="catching_up" />
48+
<PhaseHeader
49+
phase="catching_up"
50+
phaseCompletePct={phaseCompletePct}
51+
remainingSeconds={catchingUpRatesRef.current.remainingSeconds}
52+
/>
2353
<Flex direction="column" height="100%" mt="8px" gap="8px">
2454
{hasCatchingUpData && (
2555
<Flex ref={setContainerEl} direction="column" gap="5px">
2656
<BarsLabels />
2757
<CatchingUpBars catchingUpRatesRef={catchingUpRatesRef} />
2858
<BarsFooter />
29-
<BarsStats catchingUpRatesRef={catchingUpRatesRef} />
59+
<BarsStats catchingUpRates={catchingUpRatesRef.current} />
3060
</Flex>
3161
)}
3262

33-
<Card className={styles.card} mb="14px">
63+
<Flex direction="column" className={styles.card} mb="14px">
3464
<Flex gap="15px" align="center" wrap="wrap">
3565
<Text className={styles.title}>Shreds</Text>
3666
<ShredsChartLegend />
3767
</Flex>
3868
<Box flexGrow="1" minHeight="280px">
3969
<ShredsChart chartId="catching-up-shreds" isOnStartupScreen />
4070
</Box>
41-
</Card>
71+
</Flex>
4272

4373
<CatchingUpTiles />
4474
</Flex>

src/features/StartupProgress/Firedancer/CatchingUp/useCatchingUpRates.ts

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
11
import { useAtomValue } from "jotai";
22
import { useRef, useEffect } from "react";
33
import { useInterval } from "react-use";
4-
import { useValuePerSecond } from "../useValuePerSecond";
54
import { completedSlotAtom } from "../../../../api/atoms";
65
import { catchingUpStartSlotAtom, latestTurbineSlotAtom } from "./atoms";
7-
8-
const rateCalcWindowMs = 10_000;
9-
6+
import { useEmaValue } from "../../../../hooks/useEma";
7+
8+
export interface CatchingUpRates {
9+
targetTotalSlotsEstimate?: number;
10+
totalSlotsEstimate?: number;
11+
replaySlotsPerSecond?: number;
12+
turbineSlotsPerSecond?: number;
13+
remainingSeconds?: number;
14+
}
1015
/**
1116
* Provides a ref that estimates how many slots will be replayed in total
1217
*/
1318
export default function useCatchingUpRates() {
14-
const catchingUpRatesRef = useRef<{
15-
totalSlotsEstimate?: number;
16-
replaySlotsPerSecond?: number;
17-
turbineSlotsPerSecond?: number;
18-
}>({});
19+
const catchingUpRatesRef = useRef<CatchingUpRates>({});
1920
const startSlot = useAtomValue(catchingUpStartSlotAtom);
2021
const latestTurbineSlot = useAtomValue(latestTurbineSlotAtom);
2122
const latestReplaySlot = useAtomValue(completedSlotAtom);
2223

2324
const replaySlot =
2425
latestReplaySlot ?? (startSlot == null ? undefined : startSlot - 1);
2526

26-
const { valuePerSecond: replayRate } = useValuePerSecond(
27-
replaySlot,
28-
rateCalcWindowMs,
29-
);
30-
const { valuePerSecond: turbineRate } = useValuePerSecond(
31-
latestTurbineSlot,
32-
rateCalcWindowMs,
33-
);
27+
const replayRate = useEmaValue(replaySlot);
28+
const turbineRate = useEmaValue(latestTurbineSlot);
29+
30+
catchingUpRatesRef.current.replaySlotsPerSecond = replayRate;
31+
catchingUpRatesRef.current.turbineSlotsPerSecond = turbineRate;
3432

3533
// initialize estimate of how many slots we'll need to replay
3634
// determines initial widths
@@ -44,7 +42,7 @@ export default function useCatchingUpRates() {
4442

4543
const replaySlotsPerSecond = 400;
4644
const turbineSlotsPerSecond = 100;
47-
const totalSlotsEsimtate = calculateTotalSlots(
45+
const totalSlotsEstimate = calculateTotalSlots(
4846
replaySlotsPerSecond,
4947
turbineSlotsPerSecond,
5048
startSlot,
@@ -53,9 +51,7 @@ export default function useCatchingUpRates() {
5351
);
5452

5553
catchingUpRatesRef.current = {
56-
totalSlotsEstimate: totalSlotsEsimtate,
57-
replaySlotsPerSecond,
58-
turbineSlotsPerSecond,
54+
totalSlotsEstimate,
5955
};
6056
}, [latestReplaySlot, latestTurbineSlot, startSlot, catchingUpRatesRef]);
6157

@@ -81,6 +77,18 @@ export default function useCatchingUpRates() {
8177
latestTurbineSlot,
8278
);
8379

80+
const remainingReplaySlots =
81+
latestReplaySlot == null || newEstimate == null
82+
? undefined
83+
: newEstimate + startSlot - 1 - latestReplaySlot;
84+
const remainingSeconds =
85+
replayRate === 0 || remainingReplaySlots == null
86+
? undefined
87+
: remainingReplaySlots / replayRate;
88+
89+
catchingUpRatesRef.current.remainingSeconds = remainingSeconds;
90+
91+
// only update total estimate (determining number of bars) if decreasing estimate
8492
if (!newEstimate || newEstimate >= prevEstimate) return;
8593

8694
// decrement gradually
@@ -90,11 +98,8 @@ export default function useCatchingUpRates() {
9098
);
9199

92100
const updatedEstimate = prevEstimate - diffToApply;
93-
catchingUpRatesRef.current = {
94-
totalSlotsEstimate: updatedEstimate,
95-
replaySlotsPerSecond: replayRate,
96-
turbineSlotsPerSecond: turbineRate,
97-
};
101+
102+
catchingUpRatesRef.current.totalSlotsEstimate = updatedEstimate;
98103
}, 500);
99104

100105
return catchingUpRatesRef;

0 commit comments

Comments
 (0)