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
2 changes: 2 additions & 0 deletions src/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ export const peersAtom = atomWithImmer<Record<string, Peer>>({});

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

export const peersCountAtom = atom((get) => get(peersListAtom).length);

export const peersAtomFamily = atomFamily((peer?: string) =>
atom((get) => (peer !== undefined ? get(peersAtom)[peer] : undefined)),
);
Expand Down
13 changes: 7 additions & 6 deletions src/features/StartupProgress/Firedancer/Body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import {
isStartupProgressExpandedAtom,
showStartupProgressAtom,
} from "../atoms";
import { Flex } from "@radix-ui/themes";
import { Box, Flex } from "@radix-ui/themes";
import clsx from "clsx";
import { Header } from "./Header";
import { Header } from "./PhaseHeader/Header";
import { BootPhaseEnum } from "../../../api/entities";
import { bootProgressContainerElAtom } from "../../../atoms";
import Gossip from "./Gossip";
Expand Down Expand Up @@ -56,29 +56,30 @@ function BootProgressContent({ phase }: BootProgressContentProps) {
const isNarrow = useMedia("(max-width: 750px)");

return (
<Flex
<Box
ref={(el: HTMLDivElement) => setBootProgressContainerEl(el)}
direction="column"
overflowY="auto"
className={clsx(styles.container, phaseClass, {
[styles.collapsed]: !showStartupProgress || !isStartupProgressExpanded,
})}
>
<Flex
direction="column"
height="100%"
width="100%"
maxWidth={appMaxWidth}
mx="auto"
px={isNarrow ? "20px" : "89px"}
pb="20px"
>
<Header />

{phase === BootPhaseEnum.joining_gossip && <Gossip />}
{(phase === BootPhaseEnum.loading_full_snapshot ||
phase === BootPhaseEnum.loading_incremental_snapshot) && <Snapshot />}
{phase === BootPhaseEnum.catching_up && <CatchingUp />}

<Box pb="20px" />
</Flex>
</Flex>
</Box>
);
}
13 changes: 5 additions & 8 deletions src/features/StartupProgress/Firedancer/CatchingUp/BarsStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,18 @@ import { useAtomValue } from "jotai";
import styles from "./catchingUp.module.css";
import { catchingUpStartSlotAtom, latestTurbineSlotAtom } from "./atoms";
import { completedSlotAtom } from "../../../../api/atoms";
import type { CatchingUpRates } from "./useCatchingUpRates";

interface CatchingUpBarsProps {
catchingUpRatesRef: React.MutableRefObject<{
totalSlotsEstimate?: number;
replaySlotsPerSecond?: number;
turbineSlotsPerSecond?: number;
}>;
catchingUpRates: CatchingUpRates;
}
export function BarsStats({ catchingUpRatesRef }: CatchingUpBarsProps) {
export function BarsStats({ catchingUpRates }: CatchingUpBarsProps) {
const startSlot = useAtomValue(catchingUpStartSlotAtom);
const latestTurbineSlot = useAtomValue(latestTurbineSlotAtom);
const latestReplaySlot = useAtomValue(completedSlotAtom);

const replayRate = catchingUpRatesRef.current.replaySlotsPerSecond;
const turbineHeadRate = catchingUpRatesRef.current.turbineSlotsPerSecond;
const replayRate = catchingUpRates.replaySlotsPerSecond;
const turbineHeadRate = catchingUpRates.turbineSlotsPerSecond;
const catchUpRate =
replayRate == null || turbineHeadRate == null
? undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ import { Box } from "@radix-ui/themes";
import { useThrottledCallback } from "use-debounce";
import { completedSlotAtom } from "../../../../api/atoms";
import { useMeasure } from "react-use";
import type { CatchingUpRates } from "./useCatchingUpRates";

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

interface CatchingUpBarsProps {
catchingUpRatesRef: React.MutableRefObject<{
totalSlotsEstimate?: number;
replaySlotsPerSecond?: number;
turbineSlotsPerSecond?: number;
}>;
catchingUpRatesRef: React.MutableRefObject<CatchingUpRates>;
}
export function CatchingUpBars({ catchingUpRatesRef }: CatchingUpBarsProps) {
const [measureRef, measureRect] = useMeasure<HTMLDivElement>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
padding: 14px;
gap: 14px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(234, 103, 103, 0.05);
border-radius: 8px;
background: rgba(250, 250, 250, 0.05);
color: var(--boot-progress-primary-text-color);
}

Expand Down
44 changes: 37 additions & 7 deletions src/features/StartupProgress/Firedancer/CatchingUp/index.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,74 @@
import { Box, Card, Flex, Text } from "@radix-ui/themes";
import { Box, Flex, Text } from "@radix-ui/themes";
import { CatchingUpBars } from "./CatchingUpBars";
import { BarsFooter } from "./BarsFooter";
import BarsLabels from "./BarsLabels";
import { useAtomValue, useSetAtom } from "jotai";
import { catchingUpContainerElAtom, hasCatchingUpDataAtom } from "./atoms";
import {
catchingUpContainerElAtom,
catchingUpStartSlotAtom,
hasCatchingUpDataAtom,
latestTurbineSlotAtom,
} from "./atoms";
import ShredsChart from "../../../Overview/ShredsProgression/ShredsChart";
import styles from "./catchingUp.module.css";
import CatchingUpTiles from "./CatchingUpTiles";
import { PhaseHeader } from "../PhaseHeader";
import PhaseHeader from "../PhaseHeader";
import useEstimateTotalSlots from "./useCatchingUpRates";
import { BarsStats } from "./BarsStats";
import { ShredsChartLegend } from "../../../Overview/ShredsProgression/ShredsChartLegend";
import { completedSlotAtom } from "../../../../api/atoms";
import { useMemo } from "react";

export default function CatchingUp() {
const setContainerEl = useSetAtom(catchingUpContainerElAtom);
const hasCatchingUpData = useAtomValue(hasCatchingUpDataAtom);
const catchingUpRatesRef = useEstimateTotalSlots();

const startSlot = useAtomValue(catchingUpStartSlotAtom);
const latestTurbineSlot = useAtomValue(latestTurbineSlotAtom);
const latestReplaySlot = useAtomValue(completedSlotAtom);

const phaseCompletePct = useMemo(() => {
if (
startSlot == null ||
latestTurbineSlot == null ||
latestReplaySlot == null
) {
return 0;
}

const totalSlotsToReplay = latestTurbineSlot - startSlot + 1;
if (!totalSlotsToReplay) return 0;

const replayedSlots = latestReplaySlot - startSlot + 1;
return (100 * replayedSlots) / totalSlotsToReplay;
}, [latestReplaySlot, latestTurbineSlot, startSlot]);
return (
<>
<PhaseHeader phase="catching_up" />
<PhaseHeader
phase="catching_up"
phaseCompletePct={phaseCompletePct}
remainingSeconds={catchingUpRatesRef.current.remainingSeconds}
/>
<Flex direction="column" height="100%" mt="8px" gap="8px">
{hasCatchingUpData && (
<Flex ref={setContainerEl} direction="column" gap="5px">
<BarsLabels />
<CatchingUpBars catchingUpRatesRef={catchingUpRatesRef} />
<BarsFooter />
<BarsStats catchingUpRatesRef={catchingUpRatesRef} />
<BarsStats catchingUpRates={catchingUpRatesRef.current} />
</Flex>
)}

<Card className={styles.card} mb="14px">
<Flex direction="column" className={styles.card} mb="14px">
<Flex gap="15px" align="center" wrap="wrap">
<Text className={styles.title}>Shreds</Text>
<ShredsChartLegend />
</Flex>
<Box flexGrow="1" minHeight="280px">
<ShredsChart chartId="catching-up-shreds" isOnStartupScreen />
</Box>
</Card>
</Flex>

<CatchingUpTiles />
</Flex>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
import { useAtomValue } from "jotai";
import { useRef, useEffect } from "react";
import { useInterval } from "react-use";
import { useValuePerSecond } from "../useValuePerSecond";
import { completedSlotAtom } from "../../../../api/atoms";
import { catchingUpStartSlotAtom, latestTurbineSlotAtom } from "./atoms";

const rateCalcWindowMs = 10_000;

import { useEmaValue } from "../../../../hooks/useEma";

export interface CatchingUpRates {
targetTotalSlotsEstimate?: number;
totalSlotsEstimate?: number;
replaySlotsPerSecond?: number;
turbineSlotsPerSecond?: number;
remainingSeconds?: number;
}
/**
* Provides a ref that estimates how many slots will be replayed in total
*/
export default function useCatchingUpRates() {
const catchingUpRatesRef = useRef<{
totalSlotsEstimate?: number;
replaySlotsPerSecond?: number;
turbineSlotsPerSecond?: number;
}>({});
const catchingUpRatesRef = useRef<CatchingUpRates>({});
const startSlot = useAtomValue(catchingUpStartSlotAtom);
const latestTurbineSlot = useAtomValue(latestTurbineSlotAtom);
const latestReplaySlot = useAtomValue(completedSlotAtom);

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

const { valuePerSecond: replayRate } = useValuePerSecond(
replaySlot,
rateCalcWindowMs,
);
const { valuePerSecond: turbineRate } = useValuePerSecond(
latestTurbineSlot,
rateCalcWindowMs,
);
const replayRate = useEmaValue(replaySlot);
const turbineRate = useEmaValue(latestTurbineSlot);

catchingUpRatesRef.current.replaySlotsPerSecond = replayRate;
catchingUpRatesRef.current.turbineSlotsPerSecond = turbineRate;

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

const replaySlotsPerSecond = 400;
const turbineSlotsPerSecond = 100;
const totalSlotsEsimtate = calculateTotalSlots(
const totalSlotsEstimate = calculateTotalSlots(
replaySlotsPerSecond,
turbineSlotsPerSecond,
startSlot,
Expand All @@ -53,9 +51,7 @@ export default function useCatchingUpRates() {
);

catchingUpRatesRef.current = {
totalSlotsEstimate: totalSlotsEsimtate,
replaySlotsPerSecond,
turbineSlotsPerSecond,
totalSlotsEstimate,
};
}, [latestReplaySlot, latestTurbineSlot, startSlot, catchingUpRatesRef]);

Expand All @@ -81,6 +77,18 @@ export default function useCatchingUpRates() {
latestTurbineSlot,
);

const remainingReplaySlots =
latestReplaySlot == null || newEstimate == null
? undefined
: newEstimate + startSlot - 1 - latestReplaySlot;
const remainingSeconds =
replayRate === 0 || remainingReplaySlots == null
? undefined
: remainingReplaySlots / replayRate;

catchingUpRatesRef.current.remainingSeconds = remainingSeconds;

// only update total estimate (determining number of bars) if decreasing estimate
if (!newEstimate || newEstimate >= prevEstimate) return;

// decrement gradually
Expand All @@ -90,11 +98,8 @@ export default function useCatchingUpRates() {
);

const updatedEstimate = prevEstimate - diffToApply;
catchingUpRatesRef.current = {
totalSlotsEstimate: updatedEstimate,
replaySlotsPerSecond: replayRate,
turbineSlotsPerSecond: turbineRate,
};

catchingUpRatesRef.current.totalSlotsEstimate = updatedEstimate;
}, 500);

return catchingUpRatesRef;
Expand Down
Loading