perf(cluster): widen the spot batching window with the rate of the feed
Every flush commits state the whole window re-renders on, and the window was a flat 50 ms — twenty full renders a second under an RBN firehose. That is paid everywhere else in the interface, and it is what a dropdown highlighting its entries a beat behind the mouse actually is. The window now follows the rate: 50 ms on a quiet cluster, where a spot should appear the moment it arrives; 200 ms past five a second; 500 ms past twenty, where half a second's delay on a line already scrolling past is invisible.
This commit is contained in:
+20
-4
@@ -2068,6 +2068,9 @@ export default function App() {
|
||||
useEffect(() => { spotStatusRef.current = spotStatus; }, [spotStatus]);
|
||||
// Mirror of spots so the log-triggered refresh reads the current list without
|
||||
// a stale closure.
|
||||
// Arrival times of the last second's spots, for the adaptive batching window
|
||||
// in the cluster:spot listener.
|
||||
const spotRateRef = useRef<number[]>([]);
|
||||
const spotsRef = useRef(spots);
|
||||
useEffect(() => { spotsRef.current = spots; }, [spots]);
|
||||
// The decoded stations, for the same reason: the status refresh and the cache
|
||||
@@ -3595,11 +3598,24 @@ export default function App() {
|
||||
const unsubSpot = EventsOn('cluster:spot', (sp: ClusterSpot) => {
|
||||
// Stage the spot; a short timer resolves its status then commits it.
|
||||
pendingSpotsRef.current.push(sp);
|
||||
// 50 ms is enough to coalesce an RBN burst into one status lookup (the
|
||||
// worked-index is in memory, so resolving is near-instant) while staying
|
||||
// imperceptible.
|
||||
// The window WIDENS with the rate of the feed.
|
||||
//
|
||||
// Every flush commits state that the whole window re-renders on, so a
|
||||
// fixed 50 ms means twenty full renders a second under an RBN firehose —
|
||||
// and that is felt everywhere else: a dropdown highlighting its entries a
|
||||
// beat late as the mouse moves down them, which is what was reported.
|
||||
//
|
||||
// A quiet cluster keeps the 50 ms: a handful of spots an hour should
|
||||
// appear the moment they arrive. A busy one is coalesced instead, and half
|
||||
// a second's delay on a line in a list that is already scrolling past is
|
||||
// not something anyone can see.
|
||||
const now = Date.now();
|
||||
spotRateRef.current = spotRateRef.current.filter((t) => now - t < 1000);
|
||||
spotRateRef.current.push(now);
|
||||
const perSec = spotRateRef.current.length;
|
||||
const window_ms = perSec > 20 ? 500 : perSec > 5 ? 200 : 50;
|
||||
if (pendingSpotTimer.current === undefined) {
|
||||
pendingSpotTimer.current = window.setTimeout(flushPendingSpots, 50);
|
||||
pendingSpotTimer.current = window.setTimeout(flushPendingSpots, window_ms);
|
||||
}
|
||||
// Self-spot: someone spotted OUR callsign — show it in the shared header
|
||||
// toast (same place as the other notifications), not a separate banner.
|
||||
|
||||
Reference in New Issue
Block a user