Эксперименты
Рефакторинг Browser Evaluate с использованием CDP
Контекст
act:evaluate выполняет предоставленный пользователем JavaScript на странице. В настоящее время он работает через Playwright (page.evaluate или locator.evaluate). Playwright сериализует команды CDP для каждой страницы, поэтому зависший или долго выполняющийся evaluate может заблокировать очередь команд страницы и сделать так, что каждое последующее действие на эт ой вкладке будет выглядеть как «зависшее». PR #13498 добавляет прагматичную страховочную сеть (ограниченный evaluate, распространение прерывания и восстановление по возможности). В этом документе описывается более масштабный рефакторинг, который делает act:evaluate по своей сути изолированным от Playwright, так что зависший evaluate не может заблокировать обычные операции Playwright.
Цели
act:evaluateне может навсегда блокировать последующие действия браузера на той же вкладке.- Таймауты являются единым источником истины от начала до конца, чтобы вызывающая сторона могла полагаться на бюджет.
- Прерывание (abort) и таймаут обрабатываются одинаково как при HTTP, так и при внутрипроцессной диспетчеризации.
- Поддерживается привязка к элементу для evaluate без перевода всего остального с Playwright.
- Сохранение обратной совместимости для существующих вызывающих сторон и форматов данных.
Не цели
- Замена всех действий браузера (click, type, wait и т.д.) на реализации через CDP.
- Удаление существующей страховочной сети, введенной в PR #13498 (она остается полезным запасным вариантом).
- Введение новых небезопасных возможностей сверх существующего ограничения
browser.evaluateEnabled. - Добавление изоляции процессов (рабочий процесс/поток) для evaluate. Если после этого рефакторинга мы все еще увидим трудно восстанавливаемые зависшие состояния, это будет идеей для дальнейшей работы.
Текущая архитектура (Почему возникает зависание)
На высоком уровне:
- Вызывающие стороны отправляют
act:evaluateв сервис управления браузером. - Обработчик маршрута вызывает Playwright для выполнения JavaScript.
- Playwright сериализует команды страницы, поэтому evaluate, который никогда не завершается, блокирует очередь.
- Заблокированная очередь означает, что последующие операции click/type/wait на вкладке могут казаться зависшими.
Предлагаемая архитектура
1. Распространение дедлайна
Ввести единую концепцию бюджета и выводить все из нее:
- Вызывающая сторона устанавливает
timeoutMs(или дедлайн в будущем). - Внешний таймаут запроса, логика обработчика маршрута и бюджет выполнения внутри страницы используют один и тот же бюджет, с небольшим запасом там, где это необходимо для накладных расходов на сериализацию.
- Прерывание распространяется как
AbortSignalповсюду, чтобы отмена была согласованной.
Направление реализации:
- Добавить небольшой помощник (например,
createBudget({ timeoutMs, signal })), который возвращает:signal: связанный AbortSignaldeadlineAtMs: абсолютный дедлайнremainingMs(): оставшийся бюджет для дочерних операций
- Использовать этого помощника в:
src/browser/client-fetch.ts(HTTP и внутрипроцессная диспетчеризация)src/node-host/runner.ts(путь прокси)- реализациях действий браузера (Playwright и CDP)
2. Отдельный движок Evaluate (Путь CDP)
Добавить реализацию evaluate на основе CDP, которая не использует общую очередь команд Playwright для каждой страницы. Ключевое свойство заключается в том, что транспорт для evaluate — это отдельное WebSocket-соединение и отдельная сессия CDP, подключенная к цели. Направление реализации:
- Новый мо дуль, например
src/browser/cdp-evaluate.ts, который:- Подключается к настроенной конечной точке CDP (сокет на уровне браузера).
- Использует
Target.attachToTarget({ targetId, flatten: true })для полученияsessionId. - Выполняет либо:
Runtime.evaluateдля evaluate на уровне страницы, либоDOM.resolveNodeплюсRuntime.callFunctionOnдля evaluate элемента.
- При таймауте или прерывании:
- Отправляет
Runtime.terminateExecutionпо возможности для сессии. - Закрывает WebSocket и возвращает понятную ошибку.
- Отправляет
Примечания:
- Это все еще выполняет JavaScript на странице, поэтому прерывание может иметь побочные эффекты. Выигрыш в том, что это не блокирует очередь Playwright, и его можно отменить на транспортном уровне, убив сессию CDP.