mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
fix(engine): escalate hung browser lease closes
This commit is contained in:
@@ -32,6 +32,8 @@ interface BrowserPoolEntry {
|
||||
result?: BrowserLaunchResult;
|
||||
launchPromise: Promise<BrowserLaunchResult>;
|
||||
closePromise?: Promise<void>;
|
||||
forceCloseRequested: boolean;
|
||||
forceClose?: () => void;
|
||||
}
|
||||
|
||||
export interface BrowserLeasePoolOptions {
|
||||
@@ -165,6 +167,7 @@ export class BrowserLeasePool {
|
||||
state: "launching",
|
||||
refCount: 1,
|
||||
closeRequested: false,
|
||||
forceCloseRequested: false,
|
||||
launchPromise: undefined as unknown as Promise<BrowserLaunchResult>,
|
||||
};
|
||||
entry.launchPromise = this.options.launch(fingerprint).then(
|
||||
@@ -207,7 +210,9 @@ export class BrowserLeasePool {
|
||||
await entry.closePromise;
|
||||
},
|
||||
forceRelease: () => {
|
||||
deactivate(true);
|
||||
if (!deactivate(true) && entry.refCount === 0 && entry.closePromise) {
|
||||
this.requestClose(entry, true);
|
||||
}
|
||||
},
|
||||
};
|
||||
const leases = this.leasesByBrowser.get(result.browser) ?? new Set<BrowserLease>();
|
||||
@@ -217,20 +222,34 @@ export class BrowserLeasePool {
|
||||
}
|
||||
|
||||
private requestClose(entry: BrowserPoolEntry, force: boolean): void {
|
||||
if (force) {
|
||||
entry.forceCloseRequested = true;
|
||||
entry.forceClose?.();
|
||||
}
|
||||
if (entry.closePromise) return;
|
||||
entry.closeRequested = true;
|
||||
if (this.available.get(entry.key) === entry) this.available.delete(entry.key);
|
||||
entry.closePromise = entry.launchPromise
|
||||
.then(async (result) => {
|
||||
entry.state = "closing";
|
||||
if (force) {
|
||||
if (entry.forceCloseRequested) {
|
||||
this.options.forceClose(result.browser);
|
||||
} else {
|
||||
await this.options.close(result.browser).catch(() => {});
|
||||
await Promise.race([
|
||||
this.options.close(result.browser).catch(() => {}),
|
||||
new Promise<void>((resolve) => {
|
||||
entry.forceClose = () => {
|
||||
entry.forceClose = undefined;
|
||||
this.options.forceClose(result.browser);
|
||||
resolve();
|
||||
};
|
||||
}),
|
||||
]);
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
entry.forceClose = undefined;
|
||||
this.entries.delete(entry);
|
||||
if (entry.result) this.leasesByBrowser.delete(entry.result.browser);
|
||||
});
|
||||
|
||||
@@ -103,4 +103,51 @@ describe("createCaptureSession construction ownership", () => {
|
||||
rmSync(outputDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("force-releases its browser lease when rollback browser close never settles", async () => {
|
||||
vi.useFakeTimers();
|
||||
const outputDir = mkdtempSync(join(tmpdir(), "hf-session-browser-timeout-"));
|
||||
const page = {
|
||||
evaluateOnNewDocument: vi.fn().mockRejectedValue(new Error("bootstrap failed")),
|
||||
close: vi.fn().mockResolvedValue(undefined),
|
||||
} as unknown as Page;
|
||||
const disconnect = vi.fn();
|
||||
const browser = {
|
||||
connected: true,
|
||||
newPage: vi.fn().mockResolvedValue(page),
|
||||
version: vi.fn().mockResolvedValue("HeadlessChrome/150.0.0.0"),
|
||||
close: vi.fn().mockReturnValue(new Promise<void>(() => {})),
|
||||
disconnect,
|
||||
process: () => null,
|
||||
} as unknown as Browser;
|
||||
_setPuppeteerForTests({
|
||||
launch: vi.fn().mockResolvedValue(browser),
|
||||
} as unknown as PuppeteerNode);
|
||||
|
||||
try {
|
||||
const creating = expect(
|
||||
createCaptureSession(
|
||||
"http://127.0.0.1:3000",
|
||||
outputDir,
|
||||
{ width: 320, height: 180, fps: { num: 30, den: 1 }, format: "jpeg" },
|
||||
null,
|
||||
{
|
||||
browserGpuMode: "software",
|
||||
enableBrowserPool: true,
|
||||
forceScreenshot: true,
|
||||
},
|
||||
),
|
||||
).rejects.toThrow("bootstrap failed");
|
||||
|
||||
await vi.runAllTimersAsync();
|
||||
await creating;
|
||||
|
||||
expect(page.close).toHaveBeenCalledTimes(1);
|
||||
expect(browser.close).toHaveBeenCalledTimes(1);
|
||||
expect(disconnect).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
rmSync(outputDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user