next
Version:
The React Framework
116 lines (115 loc) • 4.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "setupProbeScheduler", {
enumerable: true,
get: function() {
return setupProbeScheduler;
}
});
const _usecacheprobeglobals = require("./use-cache-probe-globals");
const PROBE_THRESHOLD_MS = 10000;
const MIN_PROBE_BUDGET_MS = 3000;
function setupProbeScheduler(opts) {
const { workStore, outerRequestStore, cacheContext, encodedArguments, fillDeadlineAt, stream, abortSignal, onProbeCompleted } = opts;
if (fillDeadlineAt - performance.now() < PROBE_THRESHOLD_MS + MIN_PROBE_BUDGET_MS) {
return stream;
}
const probe = (0, _usecacheprobeglobals.getUseCacheProbe)();
if (!probe) {
return stream;
}
let lastChunkAt = performance.now();
let idleTimer;
const startProbe = ()=>{
if (abortSignal.aborted) {
return;
}
const probeStartedAtChunk = lastChunkAt;
// Computed when the probe is about to run so it gets the actual remaining
// budget, not a stale value baked in at scheduler setup. Reserves a 1s
// buffer so the probe's internal timeout fires before the outer render
// timeout.
const probeInternalTimeoutMs = fillDeadlineAt - performance.now() - 1000;
probe({
page: workStore.page,
route: workStore.route,
id: cacheContext.functionId,
kind: cacheContext.handlerKind,
encodedArguments,
// Built lazily because most fills complete before the idle timer fires;
// only worth assembling once we know the probe is running.
request: {
headers: Array.from(outerRequestStore.headers.entries()),
cookieHeader: outerRequestStore.headers.get('cookie') ?? undefined,
urlPathname: outerRequestStore.url.pathname,
urlSearch: outerRequestStore.url.search,
rootParams: outerRequestStore.rootParams ?? {},
isDraftMode: workStore.isDraftMode ?? false,
isHmrRefresh: outerRequestStore.isHmrRefresh ?? false,
hmrRefreshHash: outerRequestStore.hmrRefreshHash
},
timeoutMs: probeInternalTimeoutMs
}).then((completed)=>{
// Mid-probe recovery: chunks arrived while the probe was running, so
// the main stream is making progress. Discard the probe's result rather
// than reporting a deadlock that no longer holds.
if (lastChunkAt > probeStartedAtChunk) {
return;
}
if (completed && !abortSignal.aborted) {
onProbeCompleted();
}
}, ()=>{
// Probe failures are inconclusive; fall back to the regular cache-fill
// timeout.
});
};
const scheduleAfterIdle = ()=>{
if (idleTimer !== undefined || abortSignal.aborted) {
return;
}
const now = performance.now();
const idleFor = now - lastChunkAt;
const wait = Math.max(0, PROBE_THRESHOLD_MS - idleFor);
// Skip scheduling if the outer fill timeout will fire before the probe
// could even start running with at least a minimum useful budget. Without
// this check, a chunk arriving late in the fill could reschedule a probe
// that the outer timeout would then abort — wasted worker spawn for a probe
// that can't meaningfully complete.
if (fillDeadlineAt - now < wait + MIN_PROBE_BUDGET_MS) {
return;
}
idleTimer = setTimeout(()=>{
idleTimer = undefined;
if (abortSignal.aborted) {
return;
}
const idleNow = performance.now() - lastChunkAt;
if (idleNow < PROBE_THRESHOLD_MS) {
// A chunk arrived since we set this timer; reschedule.
scheduleAfterIdle();
return;
}
startProbe();
}, wait);
};
abortSignal.addEventListener('abort', ()=>{
if (idleTimer !== undefined) {
clearTimeout(idleTimer);
idleTimer = undefined;
}
}, {
once: true
});
scheduleAfterIdle();
return stream.pipeThrough(new TransformStream({
transform (chunk, controller) {
lastChunkAt = performance.now();
scheduleAfterIdle();
controller.enqueue(chunk);
}
}));
}
//# sourceMappingURL=use-cache-probe-scheduler.js.map