fix(daemon): add backpressure control and command serialization to prevent IPC EAGAIN (#529)
- Add AGENT_BROWSER_DEFAULT_TIMEOUT env var to override Playwright's default 60s timeout (CDP/recording 10s timeouts unaffected) - Add backpressure-aware safeWrite() that waits for drain when socket buffer is full, preventing data loss under load - Serialize command execution per socket via queue to prevent concurrent writes that cause buffer contention These daemon-side fixes complement #329 (CLI-side EAGAIN retry) by addressing the root causes: Playwright operations that outlast the CLI's IPC timeout, and concurrent socket.write() calls that overflow the kernel buffer. Tested with heavy React app (1000+ DOM nodes) — 10 consecutive snapshot commands complete without os error 35/11. Refs #322 Co-authored-by: shohu <shohu@users.noreply.github.com>
This commit is contained in:
+21
-5
@@ -28,6 +28,22 @@ import {
|
||||
ENCRYPTION_KEY_ENV,
|
||||
} from './state-utils.js';
|
||||
|
||||
/**
|
||||
* Returns the default Playwright timeout for standard operations.
|
||||
* Can be overridden via the AGENT_BROWSER_DEFAULT_TIMEOUT environment variable.
|
||||
* CDP and recording contexts use a shorter fixed timeout (10s) and are not affected.
|
||||
*/
|
||||
function getDefaultTimeout(): number {
|
||||
const envValue = process.env.AGENT_BROWSER_DEFAULT_TIMEOUT;
|
||||
if (envValue) {
|
||||
const parsed = parseInt(envValue, 10);
|
||||
if (!isNaN(parsed) && parsed > 0) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return 60000;
|
||||
}
|
||||
|
||||
// Screencast frame data from CDP
|
||||
export interface ScreencastFrame {
|
||||
data: string; // base64 encoded image
|
||||
@@ -264,7 +280,7 @@ export class BrowserManager {
|
||||
context = await this.browser.newContext({
|
||||
...(this.colorScheme && { colorScheme: this.colorScheme }),
|
||||
});
|
||||
context.setDefaultTimeout(60000);
|
||||
context.setDefaultTimeout(getDefaultTimeout());
|
||||
this.contexts.push(context);
|
||||
this.setupContextTracking(context);
|
||||
} else {
|
||||
@@ -1018,7 +1034,7 @@ export class BrowserManager {
|
||||
this.kernelSessionId = session.session_id;
|
||||
this.kernelApiKey = kernelApiKey;
|
||||
this.browser = browser;
|
||||
context.setDefaultTimeout(60000);
|
||||
context.setDefaultTimeout(getDefaultTimeout());
|
||||
this.contexts.push(context);
|
||||
this.pages.push(page);
|
||||
this.activePageIndex = 0;
|
||||
@@ -1091,7 +1107,7 @@ export class BrowserManager {
|
||||
this.browserUseSessionId = session.id;
|
||||
this.browserUseApiKey = browserUseApiKey;
|
||||
this.browser = browser;
|
||||
context.setDefaultTimeout(60000);
|
||||
context.setDefaultTimeout(getDefaultTimeout());
|
||||
this.contexts.push(context);
|
||||
this.pages.push(page);
|
||||
this.activePageIndex = 0;
|
||||
@@ -1346,7 +1362,7 @@ export class BrowserManager {
|
||||
});
|
||||
}
|
||||
|
||||
context.setDefaultTimeout(60000);
|
||||
context.setDefaultTimeout(getDefaultTimeout());
|
||||
this.contexts.push(context);
|
||||
this.setupContextTracking(context);
|
||||
|
||||
@@ -1678,7 +1694,7 @@ export class BrowserManager {
|
||||
viewport: viewport === undefined ? { width: 1280, height: 720 } : viewport,
|
||||
...(this.colorScheme && { colorScheme: this.colorScheme }),
|
||||
});
|
||||
context.setDefaultTimeout(60000);
|
||||
context.setDefaultTimeout(getDefaultTimeout());
|
||||
this.contexts.push(context);
|
||||
this.setupContextTracking(context);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user