Files
psysonic/src/hooks/useSidebarPerfProbe.ts
T
cucadmuh 2224ddbe78 feat(perf): add on-demand (ui) throughput to cover pipeline cpm (#947)
* feat(perf): add on-demand (ui) throughput to cover pipeline cpm

Cover cpm previously measured only the native backfill (lib) via the
cover:library-progress done delta. Add a parallel UI series: every completed
on-demand Rust cover ensure (grid / now-playing) records a timestamp, surfaced
as a covers-per-minute rate. Both are exposed in the live cover diag, shown as
separate Backfill (lib) / On-demand (ui) cards in the Monitor tab (each pinnable
to the overlay) and as lib/ui rows in the Cover pipeline overlay block.

* docs(changelog): note cover on-demand (ui) throughput (PR #947)

Add CHANGELOG entry and credits line for the UI cover cpm metric.

* fix(perf): source on-demand (ui) cpm from backend produced-cover count

The JS ensure-queue counter never tracked: produced covers return hit:true
(only misses/errors are hit:false), and ensure-queue dedup/HMR made client
counting unreliable. Count on-demand covers natively in ensure_inner on the
produce path (non-bulk, past the cache-hit gate), expose a cumulative
uiEnsuredTotal in the pipeline stats, and derive the per-minute rate on the
frontend from polled deltas — mirroring the lib backfill series.

* perf(cover): measure cpm over trailing 5s instead of full minute

A 60s rolling average added too much inertia, flattening real bursts and
stalls in both the lib backfill and on-demand (ui) cover throughput. Compute
the rate from the trailing 5s of samples (still extrapolated to per-minute),
so the figure reacts promptly and decays to 0 within the window when idle.
2026-06-02 12:11:22 +03:00

120 lines
3.8 KiB
TypeScript

import { useEffect, useState } from 'react';
import { acquirePerfLivePoll, patchPerfLiveAnalysis } from '../utils/perf/perfLiveStore';
import { setPerfProbeTelemetryActive } from '../utils/perf/perfTelemetry';
import { useAnalysisPerfLast } from '../utils/perf/analysisPerfStore';
import { useAnalysisPerfListener } from './useAnalysisPerfListener';
import { useCoverPerfListener, useCoverUiThroughputPoll } from './useCoverPerfListener';
import {
getPerfProbeFlags,
subscribePerfProbeFlags,
} from '../utils/perf/perfFlags';
import { hasAnyLiveMetricPollNeed, usePerfLiveOverlayPins } from '../utils/perf/perfOverlayPins';
import { useSyncExternalStore } from 'react';
interface Result {
perfProbeOpen: boolean;
setPerfProbeOpen: (open: boolean) => void;
}
function useNeedAnalysisTelemetry(perfProbeOpen: boolean, livePins: ReadonlySet<string>): boolean {
return useSyncExternalStore(
subscribePerfProbeFlags,
() => {
const flags = getPerfProbeFlags();
return (
perfProbeOpen
|| flags.showAnalysisPerfOverlay
|| livePins.has('analysis:tpm')
|| livePins.has('analysis:last')
);
},
() => perfProbeOpen,
);
}
function useNeedCoverTelemetry(perfProbeOpen: boolean, livePins: ReadonlySet<string>): boolean {
return useSyncExternalStore(
subscribePerfProbeFlags,
() => (
perfProbeOpen
|| getPerfProbeFlags().showCoverPerfOverlay
|| livePins.has('cover:cpm')
|| livePins.has('cover:cpm:ui')
),
() => perfProbeOpen,
);
}
/** Wires Ctrl+Shift+D probe modal and shared live metric polling. */
export function useSidebarPerfProbe(): Result {
const [perfProbeOpen, setPerfProbeOpen] = useState(false);
const livePins = usePerfLiveOverlayPins();
const analysisLast = useAnalysisPerfLast();
const needAnalysis = useNeedAnalysisTelemetry(perfProbeOpen, livePins);
const needCover = useNeedCoverTelemetry(perfProbeOpen, livePins);
useAnalysisPerfListener(needAnalysis);
useCoverPerfListener(needCover);
useCoverUiThroughputPoll(needCover);
useEffect(() => {
setPerfProbeTelemetryActive(perfProbeOpen);
return () => setPerfProbeTelemetryActive(false);
}, [perfProbeOpen]);
useEffect(() => {
if (!perfProbeOpen) return;
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') setPerfProbeOpen(false);
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [perfProbeOpen]);
useEffect(() => {
const releases: Array<() => void> = [];
if (perfProbeOpen) releases.push(acquirePerfLivePoll('modal'));
if (hasAnyLiveMetricPollNeed()) releases.push(acquirePerfLivePoll('overlay-pins'));
if (releases.length === 0) return;
return () => releases.forEach(release => release());
}, [perfProbeOpen, livePins.size]);
useEffect(() => {
patchPerfLiveAnalysis({
lastTotalMs: analysisLast?.totalMs ?? null,
lastFetchMs: analysisLast?.fetchMs ?? null,
lastSeedMs: analysisLast?.seedMs ?? null,
lastBpmMs: analysisLast?.bpmMs ?? null,
});
}, [
analysisLast?.at,
analysisLast?.totalMs,
analysisLast?.fetchMs,
analysisLast?.seedMs,
analysisLast?.bpmMs,
]);
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if (!(e.ctrlKey && e.shiftKey && !e.altKey && !e.metaKey)) return;
if (e.key.toLowerCase() !== 'd') return;
const target = e.target as HTMLElement | null;
if (target && (
target.tagName === 'INPUT'
|| target.tagName === 'TEXTAREA'
|| target.tagName === 'SELECT'
|| target.isContentEditable
)) return;
e.preventDefault();
setPerfProbeOpen(true);
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, []);
return {
perfProbeOpen,
setPerfProbeOpen,
};
}