perf(browser): stop the reclaim sweep once nothing is resident

The sweep only stopped when the last page was closed, so a host whose pages had
all been parked kept waking every interval to find nothing to reclaim — on a
headless server that is meant to sit idle cheaply. It now stops as soon as
nothing is resident, and a wake reschedules it, because a woken page has to
become reclaimable again.

The timer only ever served the idle evictor. The cap is inherently event-driven
— residency changes only on create and wake — but a page going idle produces no
event by definition, so something has to look. Polling is the cheaper form of
looking here rather than the lazier one: activity is recorded on every command,
so per-page deadlines would be rescheduled far more often than the interval
fires.
This commit is contained in:
Jinwoo-H
2026-08-19 19:07:14 -04:00
parent 58e0960598
commit 85c7a44ee3
3 changed files with 32 additions and 0 deletions
@@ -489,6 +489,24 @@ describe('OffscreenBrowserBackend reclamation', () => {
expect(createTimeout).toBeUndefined()
})
it('stops sweeping once nothing is resident, and resumes on wake', async () => {
// Why: an idle headless host should not keep waking every interval to find
// nothing to reclaim. Waking has to bring the sweep back or the woken page
// would never be reclaimed again.
const h = createHarness()
await h.backend.createTab({ url: 'https://a', browserPageId: 'a' })
const reclaimer = (h.backend as unknown as { reclaimer: { isScheduled: boolean } }).reclaimer
expect(reclaimer.isScheduled).toBe(true)
h.clock.value += 120_000
await h.backend.reclaimIdlePages()
expect(h.backend.listParkedPages()).toHaveLength(1)
expect(reclaimer.isScheduled).toBe(false)
await h.backend.wakeTab('a')
expect(reclaimer.isScheduled).toBe(true)
})
it('coalesces concurrent wakes into one renderer', async () => {
const h = createHarness()
await h.backend.createTab({ url: 'https://example.test/a' })
@@ -185,6 +185,9 @@ export class OffscreenBrowserBackend implements BrowserBackend {
return false
}
page.lastActivityAt = this.now()
// Why: the sweep stops once nothing is resident, so a wake has to bring
// it back or this page would never be reclaimed again.
this.reclaimer.ensureScheduled()
return true
})()
this.waking.set(browserPageId, wake)
@@ -60,9 +60,20 @@ export class OffscreenBrowserPageReclaimer {
await this.deps.park(browserPageId)
parked.push(browserPageId)
}
// Why: with nothing resident there is nothing left to reclaim, so the sweep
// has no work until a page is created or woken — both of which reschedule
// it. Otherwise an idle host keeps waking up forever to find nothing.
if (this.deps.pages.resident().length === 0) {
this.stop()
}
return parked
}
/** Whether a sweep is currently scheduled. Exposed so the owner can assert it. */
get isScheduled(): boolean {
return this.sweepTimer !== null
}
ensureScheduled(): void {
if (this.sweepTimer) {
return