Skip to content

Commit f79a364

Browse files
fix(live): de-dupe /sdk-info on resume + don't re-poll /broadcast on every tab focus
Two redundant-call fixes on the public live page: 1. /api/public/sdk-info fired 5x on resume, 1x on fresh load. refreshSdkInfoIfNeeded set its dedupe key (_sdkInfoFetchedFor) only AFTER the await resolved, so the burst of state updates on resume (REST seed + SSE state ticks + loadCurrentBatch), each calling applyLiveModeBadge → refreshSdkInfoIfNeeded, all passed the guard before the first fetch returned and each issued its own request. Share one in-flight promise (_sdkInfoInflight) so concurrent callers coalesce to one fetch. 2. /api/broadcast fired on every tab focus. The visibilitychange handler called checkBroadcastState unconditionally. Track the last poll time and only catch up on focus when >= BROADCAST_POLL_MS (10s) has elapsed — so a quick tab flick makes no extra call, while a return after a long hidden spell (when the interval was throttled) still re-checks for a run that started meanwhile.
1 parent 8054d3c commit f79a364

1 file changed

Lines changed: 33 additions & 10 deletions

File tree

live.html

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,11 @@ <h1 class="live-page-title">
866866
// /api/broadcast itself — we don't connect SSE or hit any work endpoint.
867867
let _activeRun = false;
868868
let _liveWorkConnected = false;
869+
// When the last /api/broadcast poll fired. Lets the visibilitychange
870+
// handler skip a redundant poll on a quick tab flick — it only catches up
871+
// when the 10s interval may have been throttled during a long hidden spell.
872+
let _lastBroadcastPollAt = 0;
873+
const BROADCAST_POLL_MS = 10000;
869874

870875
// Fan out to the live-work endpoints — runs EXACTLY ONCE per paused→active
871876
// transition. This is the "if active, then make the necessary calls" half of
@@ -888,6 +893,7 @@ <h1 class="live-page-title">
888893
// activeRun }; activeRun gates ALL of /live's other traffic so a paused page
889894
// makes just this one call (plus its 10s repeat) until a run actually starts.
890895
async function checkBroadcastState() {
896+
_lastBroadcastPollAt = Date.now();
891897
try {
892898
const r = await fetch('/api/broadcast', { cache: 'no-store' });
893899
if (!r.ok) return;
@@ -976,9 +982,14 @@ <h1 class="live-page-title">
976982
await checkBroadcastState();
977983
// Re-poll the single endpoint so a run that STARTS while we're paused flips
978984
// the page to live within ~10s — without any traffic in the meantime.
979-
setInterval(checkBroadcastState, 10000);
985+
setInterval(checkBroadcastState, BROADCAST_POLL_MS);
986+
// Re-checking on tab focus catches a run that started while the interval
987+
// was throttled in the background — but DON'T fire on every tab switch.
988+
// Skip when the interval already polled within the last cycle so a quick
989+
// flick back to the tab makes no extra /api/broadcast call.
980990
document.addEventListener('visibilitychange', () => {
981-
if (document.visibilityState === 'visible') checkBroadcastState();
991+
if (document.visibilityState !== 'visible') return;
992+
if (Date.now() - _lastBroadcastPollAt >= BROADCAST_POLL_MS) checkBroadcastState();
982993
});
983994
});
984995

@@ -2101,17 +2112,29 @@ <h1 class="live-page-title">
21012112
// we don't want to hammer it on every SSE state tick.
21022113
let _sdkInfo = null; // { active, name, version }
21032114
let _sdkInfoFetchedFor = null;
2115+
let _sdkInfoInflight = null; // shared promise — dedupes concurrent fetches
21042116
async function refreshSdkInfoIfNeeded() {
21052117
const want = (_liveState && _liveState.activeSDK) || null;
21062118
if (want && want === _sdkInfoFetchedFor && _sdkInfo) return;
2107-
try {
2108-
const r = await fetch('/api/public/sdk-info');
2109-
if (!r.ok) return;
2110-
const d = await r.json();
2111-
_sdkInfo = d;
2112-
_sdkInfoFetchedFor = d.active || want;
2113-
applyLiveModeBadge();
2114-
} catch {}
2119+
// Dedupe concurrent calls. On RESUME a burst of state updates (REST seed,
2120+
// SSE state ticks, loadCurrentBatch) each call applyLiveModeBadge →
2121+
// refreshSdkInfoIfNeeded before the first fetch resolves and sets
2122+
// _sdkInfoFetchedFor — so all of them used to fire their own /sdk-info
2123+
// request (seen as 5 calls on resume vs 1 on a fresh load). Share one
2124+
// in-flight request instead; the key is set when it resolves.
2125+
if (_sdkInfoInflight) return _sdkInfoInflight;
2126+
_sdkInfoInflight = (async () => {
2127+
try {
2128+
const r = await fetch('/api/public/sdk-info');
2129+
if (!r.ok) return;
2130+
const d = await r.json();
2131+
_sdkInfo = d;
2132+
_sdkInfoFetchedFor = d.active || want;
2133+
applyLiveModeBadge();
2134+
} catch {}
2135+
finally { _sdkInfoInflight = null; }
2136+
})();
2137+
return _sdkInfoInflight;
21152138
}
21162139
function sdkSuffix() {
21172140
if (!_sdkInfo || !_sdkInfo.name) return '';

0 commit comments

Comments
 (0)