From 7b92f5321bba53de9c1cf51d2c80a59c10ba74a2 Mon Sep 17 00:00:00 2001 From: Ami Suzuki Date: Mon, 1 Dec 2025 22:30:39 -0600 Subject: [PATCH 1/2] chore: use server time for shreds --- src/api/atoms.ts | 2 ++ src/api/entities.ts | 6 ++++++ src/api/types.ts | 3 +++ src/api/useSetAtomWsData.ts | 6 ++++++ .../ShredsProgression/shredsProgressionPlugin.ts | 13 ++++++++++--- 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/api/atoms.ts b/src/api/atoms.ts index 79440f22..a6460a37 100644 --- a/src/api/atoms.ts +++ b/src/api/atoms.ts @@ -29,6 +29,7 @@ import type { GossipPeersSize, GossipPeersRowsUpdate, GossipPeersCellUpdate, + ServerTimeNanos, } from "./types"; import { rafAtom } from "../atomUtils"; @@ -61,6 +62,7 @@ export const optimisticallyConfirmedSlotAtom = atom< >(undefined); export const completedSlotAtom = atom(undefined); +export const serverTimeNanosAtom = atom(undefined); export const estimatedSlotAtom = atom(undefined); diff --git a/src/api/entities.ts b/src/api/entities.ts index 3f23ec08..ce4e160e 100644 --- a/src/api/entities.ts +++ b/src/api/entities.ts @@ -121,6 +121,8 @@ export const catchUpHistorySchema = z.object({ turbine: z.number().array(), }); +export const serverTimeNanosSchema = z.coerce.number(); + export const estimatedSlotSchema = z.number(); export const resetSlotSchema = z.number().nullable(); export const storageSlotSchema = z.number().nullable(); @@ -565,6 +567,10 @@ export const summarySchema = z.discriminatedUnion("key", [ key: z.literal("catch_up_history"), value: catchUpHistorySchema, }), + summaryTopicSchema.extend({ + key: z.literal("server_time_nanos"), + value: serverTimeNanosSchema, + }), ]); export const epochNewSchema = z.object({ diff --git a/src/api/types.ts b/src/api/types.ts index 3aacf659..2f9c0b79 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -57,6 +57,7 @@ import type { gossipStorageStatsSchema, gossipMessageStatsSchema, schedulerCountsSchema, + serverTimeNanosSchema, } from "./entities"; export type Client = z.infer; @@ -94,6 +95,8 @@ export type OptimisticallyConfirmedSlot = z.infer< export type CompletedSlot = z.infer; export type CatchUpHistory = z.infer; +export type ServerTimeNanos = z.infer; + // export type SlotCompleted = z.infer; export type EstimatedSlot = z.infer; diff --git a/src/api/useSetAtomWsData.ts b/src/api/useSetAtomWsData.ts index 5e4303a7..2ee403bb 100644 --- a/src/api/useSetAtomWsData.ts +++ b/src/api/useSetAtomWsData.ts @@ -27,6 +27,7 @@ import { gossipPeersSizeAtom, gossipPeersRowsUpdateAtom, gossipPeersCellUpdateAtom, + serverTimeNanosAtom, } from "./atoms"; import { blockEngineSchema, @@ -196,6 +197,7 @@ export function useSetAtomWsData() { const setBlockEngine = useSetAtom(blockEngineAtom); const setCompletedSlot = useSetAtom(completedSlotAtom); + const setServerTimeNanos = useSetAtom(serverTimeNanosAtom); const addSkippedClusterSlots = useSetAtom(addSkippedClusterSlotsAtom); const deleteSkippedClusterSlot = useSetAtom(deleteSkippedClusterSlotAtom); @@ -383,6 +385,10 @@ export function useSetAtomWsData() { addRepairSlots(value.repair); break; } + case "server_time_nanos": { + setServerTimeNanos(value); + break; + } case "root_slot": case "optimistically_confirmed_slot": case "estimated_slot": diff --git a/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts b/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts index b25c5d88..49fc9ca8 100644 --- a/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts +++ b/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts @@ -22,7 +22,8 @@ import { skippedClusterSlotsAtom } from "../../../atoms"; import { clamp } from "lodash"; import { ShredEvent } from "../../../api/entities"; import { getSlotGroupLabelId, getSlotLabelId } from "./utils"; -import { slotsPerLeader } from "../../../consts"; +import { nsPerMs, slotsPerLeader } from "../../../consts"; +import { serverTimeNanosAtom } from "../../../api/atoms"; const store = getDefaultStore(); export const shredsXScaleKey = "shredsXScaleKey"; @@ -55,10 +56,16 @@ export function shredsProgressionPlugin( const minCompletedSlot = store.get(atoms.minCompletedSlot); const skippedSlotsCluster = store.get(skippedClusterSlotsAtom); const rangeAfterStartup = store.get(atoms.rangeAfterStartup); + const serverTimeNanos = store.get(serverTimeNanosAtom); const maxX = u.scales[shredsXScaleKey].max; - if (!liveShreds || !slotRange || maxX == null) { + if ( + !liveShreds || + !slotRange || + maxX == null || + serverTimeNanos == null + ) { return; } @@ -74,7 +81,7 @@ export function shredsProgressionPlugin( } // Offset to convert shred event delta to chart x value - const delayedNow = Date.now() - delayMs; + const delayedNow = Math.trunc(serverTimeNanos / nsPerMs) - delayMs; const tsXValueOffset = delayedNow - liveShreds.referenceTs; From da95f63c47e198a7e7115b7690b75af6007747c7 Mon Sep 17 00:00:00 2001 From: Ami Suzuki Date: Tue, 2 Dec 2025 10:53:25 -0600 Subject: [PATCH 2/2] chore: check server time to delete shred slots --- src/atoms.ts | 9 ++++++++- src/features/Overview/ShredsProgression/atoms.ts | 4 ++-- .../ShredsProgression/shredsProgressionPlugin.ts | 11 +++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/atoms.ts b/src/atoms.ts index 2fcc3201..8c4ab929 100644 --- a/src/atoms.ts +++ b/src/atoms.ts @@ -1,10 +1,11 @@ import { atom } from "jotai"; -import { slotsPerLeader } from "./consts"; +import { nsPerMs, slotsPerLeader } from "./consts"; import { atomWithImmer } from "jotai-immer"; import { bootProgressAtom, estimatedSlotDurationAtom, identityKeyAtom, + serverTimeNanosAtom, skippedSlotsAtom, startupProgressAtom, } from "./api/atoms"; @@ -695,3 +696,9 @@ export const [ }), ]; })(); + +export const serverTimeMsAtom = atom((get) => { + const serverTimeNanos = get(serverTimeNanosAtom); + if (serverTimeNanos == null) return undefined; + return Math.round(serverTimeNanos / nsPerMs); +}); diff --git a/src/features/Overview/ShredsProgression/atoms.ts b/src/features/Overview/ShredsProgression/atoms.ts index 162bfa5a..1f9a91d1 100644 --- a/src/features/Overview/ShredsProgression/atoms.ts +++ b/src/features/Overview/ShredsProgression/atoms.ts @@ -5,6 +5,7 @@ import { delayMs, xRangeMs } from "./const"; import { nsPerMs, slotsPerLeader } from "../../../consts"; import { getSlotGroupLeader } from "../../../utils"; import { startupFinalTurbineHeadAtom } from "../../StartupProgress/atoms"; +import { serverTimeMsAtom } from "../../../atoms"; type ShredEventTsDeltaMs = number | undefined; /** @@ -169,11 +170,10 @@ export function createLiveShredsAtoms() { set(_liveShredsAtom, (prev) => { const slotRange = get(_slotRangeAtom); + const now = get(serverTimeMsAtom) ?? Date.now(); if (!prev || !slotRange) return prev; - const now = Date.now(); - if (isStartup) { // During startup, we only show event dots, not spans. Delete slots without events in chart view for ( diff --git a/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts b/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts index 49fc9ca8..a0cb349e 100644 --- a/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts +++ b/src/features/Overview/ShredsProgression/shredsProgressionPlugin.ts @@ -18,12 +18,11 @@ import { shredReplayStartedColor, shredSkippedColor, } from "../../../colors"; -import { skippedClusterSlotsAtom } from "../../../atoms"; +import { serverTimeMsAtom, skippedClusterSlotsAtom } from "../../../atoms"; import { clamp } from "lodash"; import { ShredEvent } from "../../../api/entities"; import { getSlotGroupLabelId, getSlotLabelId } from "./utils"; -import { nsPerMs, slotsPerLeader } from "../../../consts"; -import { serverTimeNanosAtom } from "../../../api/atoms"; +import { slotsPerLeader } from "../../../consts"; const store = getDefaultStore(); export const shredsXScaleKey = "shredsXScaleKey"; @@ -56,7 +55,7 @@ export function shredsProgressionPlugin( const minCompletedSlot = store.get(atoms.minCompletedSlot); const skippedSlotsCluster = store.get(skippedClusterSlotsAtom); const rangeAfterStartup = store.get(atoms.rangeAfterStartup); - const serverTimeNanos = store.get(serverTimeNanosAtom); + const serverTimeMs = store.get(serverTimeMsAtom); const maxX = u.scales[shredsXScaleKey].max; @@ -64,7 +63,7 @@ export function shredsProgressionPlugin( !liveShreds || !slotRange || maxX == null || - serverTimeNanos == null + serverTimeMs == null ) { return; } @@ -81,7 +80,7 @@ export function shredsProgressionPlugin( } // Offset to convert shred event delta to chart x value - const delayedNow = Math.trunc(serverTimeNanos / nsPerMs) - delayMs; + const delayedNow = serverTimeMs - delayMs; const tsXValueOffset = delayedNow - liveShreds.referenceTs;