From 242931e503211d41b0ac249299c90a04fe796acf Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Thu, 8 Oct 2026 15:16:19 +0600 Subject: [PATCH] [2026-10-08] eagle: work/projects/cpm-web-extension-breakage-session-summary-2026-10-07.md work/projects/cpm-web-extension-repro-and-fix-2026-10-08.md --- ...ion-breakage-session-summary-2026-10-07.md | 1 + ...-web-extension-repro-and-fix-2026-10-08.md | 57 ++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/work/projects/cpm-web-extension-breakage-session-summary-2026-10-07.md b/work/projects/cpm-web-extension-breakage-session-summary-2026-10-07.md index 9c0d6cd4..3eb0148d 100644 --- a/work/projects/cpm-web-extension-breakage-session-summary-2026-10-07.md +++ b/work/projects/cpm-web-extension-breakage-session-summary-2026-10-07.md @@ -257,6 +257,7 @@ Error 6 частично ложится на путь `refreshCpmDashboardState` - **Одна вкладка:** потеря `init` при первой загрузке background нового идентификатора (установка или обновление) воспроизведена, это 317981. При запуске её уже закрывает gate coordinator'а; открытыми оставались установка или обновление посреди сессии — закрыто. - **Полный отказ:** падение Network-процесса оставляет background живым, а worker мёртвым; ломаются все вкладки и уже открытые документы; M4 чинит только новые навигации. Закрыто watchdog'ом, который перезапускает только background-процесс. - **Смерть worker'а при живом Network-процессе** (SPI или удаление SW-регистраций) из приложения не обнаружить; закрыто перезапуском background по первому сбою (стоит одну навигацию). +- Сверка с данными 5 октября: смена Network-процесса заметно чаще в группе «без видимой ошибки» (iOS 17–21%, macOS 30–45% против ~10% и 1–3% в остальных группах), но это не доказательство — нужен параметр `network_replaced_since_view`. Error 6 на iOS (~1/3 строк), привязка вкладок и большая часть «здоровых» сбоев фиксами не покрыты. - Фикс не закоммичен; открытые вопросы (feature flag, телеметрия, SPI, сборка приложений, ping в extension) — в заметке по ссылке. --- diff --git a/work/projects/cpm-web-extension-repro-and-fix-2026-10-08.md b/work/projects/cpm-web-extension-repro-and-fix-2026-10-08.md index ea674f03..2d85bcc0 100644 --- a/work/projects/cpm-web-extension-repro-and-fix-2026-10-08.md +++ b/work/projects/cpm-web-extension-repro-and-fix-2026-10-08.md @@ -18,6 +18,7 @@ tags: - Harness на реальном WebKit готов: реальные `WebExtensionManager`, `WebExtensionLifecycleCoordinator` + navigation gate, `CPMMessagingHealthMonitor` (4 с), M4, production extension без изменений логики. - Оба класса отказа воспроизведены, причины найдены, фикс на стороне app написан, тесты зелёные. +- Сверка с данными 5 октября: механизм краша Network-процесса подкреплён лишь частично (20–45% «здоровых» `stuck`); error 6 на iOS (~1/3 строк), привязка вкладок и большая часть «здоровых» сбоев фиксами не покрыты. Нужна телеметрия до или вместе с фиксами (раздел «Телеметрия для валидации»). - Весь пакет: 490 тестов, 0 падений (repro-тесты без env пропускаются). SwiftLint `--strict` чистый. ## Что нашли @@ -64,6 +65,57 @@ tags: | Краш Network-процесса без M4 | не восстанавливается | работает | | Смерть worker'а (SPI / удаление регистраций) | 2 таймаута → M4, старые документы сломаны | 1 потерянная навигация, затем всё работает без reload | +## Фиксы ↔ гипотезы + +| Фикс | Гипотеза | Подтверждена данными? | +|---|---|---| +| Gate на первую загрузку посреди сессии | Потеря `init` при eager-загрузке background нового идентификатора (317981), не про Network-процесс | Нет. Косвенно — `app_version_change=updated`, но запуск и так под gate; частота установок посреди сессии неизвестна | +| Watchdog по PID Network-процесса | Краш Network-процесса → мёртвый worker при «живом» background | Частично (см. ниже) | +| Перезапуск по первому сбою | Мёртвый worker по **любой** причине при отзывчивом процессе background'а | Не проверена: из app причину не видно | + +## Сверка с собранными данными (5 октября) + +Источник: `apple-browsers.git/cpm-public-attribution-2026-10-06/events.tsv` (строки `stuck` .count) и `events_daily.tsv` (в скобках). Группы по приоритету, как в атрибуции. + +| Группа по снимку | iOS: всего / `network_restarted=true` | macOS: всего / `network_restarted=true` | +|---|---|---| +| Вкладка: controller/scripts/tab | 81 / 10% (13%) | 102 / 1% (2%) | +| Нет background view | 158 / 11% (14%) | 35 / 3% (3%) | +| Error 6 (background не загрузился) | **579** / 10% (14%) | 3 / – | +| `died_*` (процесс background умер) | 14 / 86% (85%) | 0 | +| Снимок без видимой ошибки | **1 410** / **17% (21%)** | **705** / **30% (45%)** | + +**За гипотезу о Network-процессе:** смена Network-процесса заметно чаще в группе «без видимой ошибки» — там, где её предсказывает механизм. В остальных группах ~10% на iOS и 1–3% на macOS. Смерть процесса background'а почти всегда вместе со сменой Network-процесса — похоже на jetsam на iOS, убивающий оба. + +**Почему это не доказательство:** `network_restarted` сравнивает PID с загрузкой *контекста*, а не с последним созданием background view. В почти всех таких строках последние события — `view, proxy_on` за 30 с – 5 мин до снимка. Если view пересоздан *после* смены процесса, worker свежий и механизм не применим. Порядок из текущих данных не восстановить. + +**Итог:** механизм объясняет в лучшем случае 20–45% «здоровых» `stuck`. Остальное — другие дефекты, которые текущими фиксами не покрыты: + +- **Error 6 — ~1/3 всех строк iOS.** WebKit сам сообщает, что background не загрузился (`dealloc → error:6 → view…`). Worker не поднимается, а не тихо умирает. Перезапуск по сбою почти не поможет: после error 6 view обычно нет. **Отдельный приоритет для iOS.** +- **Вкладка не привязана** (macOS: неверный controller и нет scripts; iOS: нет scripts) — привязка вкладки к контексту, фиксы не трогают. +- **Нет background view** — WebKit не пересоздал background; обоим механизмам нужен живой view. +- **«Здоровые» без смены Network-процесса — крупнейший необъяснённый остаток** (1 170 iOS / 497 macOS). Кандидаты: другая смерть worker'а (поймает перезапуск по сбою), поздний или несопоставленный `refreshCpmDashboardState` при живом worker'е (перезапуск — ложное срабатывание), первая загрузка вне gate. Без `native_seen` не разделить. + +## Телеметрия для валидации + +Выпустить раньше фиксов или вместе с ними в A/B — иначе эффект фиксов не отделить. Все значения — bool/enum/bucket, без URL и идентификаторов; оформить через `ddg-add-pixel`, privacy triage (прецедент — [[cpm-m3-telemetry-privacy-triage]]). + +**Подтвердить сбои — параметры к существующим `initialization_failed` / `stuck`:** + +1. `network_replaced_since_view` (bool) — PID Network-процесса при последнем создании background view ≠ текущему. Прямая подпись мёртвого worker'а. Recorder уже вычисляет это (`CPMMessagingDiagnosticsRecorder.swift:285-289`), но только логирует. Плюс токен `net_replaced` в `bg_events`, чтобы видеть порядок. **Самое дешёвое и самое ценное.** +2. `native_seen` (`none` / `site_check` / `refresh_other_url`) — были ли нативные вызовы от background для этой навигации (background зовёт `isFeatureEnabled` с URL вкладки при обработке `init`). Делит сбой на «не дошло до worker'а» / «worker обработал, refresh опоздал» / «рассогласование URL на native». Только app. +3. `bg_first_load` (bool) + токены `install` / `upgrade` / `bg_loaded` в `bg_events` — подпись бага первой загрузки. +4. `gate` (`none` / `launch` / `startup` / `timeout`) — прошла ли навигация мимо gate или он открылся по таймауту 5 с. + +**Подтвердить, что фиксы работают:** + +1. Пиксель `debug_web_extension_cpm_background_restarted` (daily+count): `trigger` = `network_process_replaced` / `initialization_failed`; `outcome` = `restarted` / `skipped_unresponsive` / `spi_unavailable`. +2. Атрибуция восстановления: `recovery_action` (`none` / `background_restart`) к `recovered_without_extension_reload`, либо пара `recovered_after_background_restart` / `background_restart_failed` (следующая навигация после перезапуска тоже упала → M4). +3. Ожидаемый эффект: падают `messaging_stuck` и M4 (`recovered_after_extension_reload`); почти исчезают вторые сбои эпизода с `native_seen=none` + `network_replaced_since_view=true`; исчезают сбои с `bg_first_load=true` вне запуска. +4. **A/B, а не просто флаг:** recovery и новый gate — за экспериментальным флагом (`ddg-add-feature-flag`), когорта в CPM-пикселях. Сравнение версий (1.208 → 1.209) уже оказалось неинформативным. + +Не измерить без extension: восстановление уже открытых документов (монитор меряет только навигации) и причину смерти worker'а при живом Network-процессе. + ## Тесты Папка `Tests/WebExtensionsTests/CPMRepro/`, запуск из `SharedPackages/WebExtensions`: @@ -77,7 +129,10 @@ tags: - [ ] **Feature flag.** Watchdog и перезапуск по первому сбою сейчас включены по умолчанию (`isEmbeddedBackgroundRecoveryEnabled = { true }`), в приложениях не подключены к удалённому флагу. Завести флаг (`ddg-add-feature-flag`), желательно раздельно для watchdog и для перезапуска по сбою. - [ ] **Риск перезапуска по первому сбою.** Первых сбоев много (~6.6k установок/день на iOS); часть — не мёртвый worker (нет инъекции, медленный native). Перезапуск дешевле M4, но рвёт незавершённую работу background для других вкладок. Решить: включать сразу или через эксперимент. -- [ ] **Телеметрия.** Восстановление после перезапуска сейчас попадает в `recovered_without_extension_reload` и неотличимо. Нужен пиксель или параметр «background restarted» с причиной (network process replaced / first failure) — `ddg-add-pixel`. +- [ ] **Телеметрия.** Восстановление после перезапуска сейчас попадает в `recovered_without_extension_reload` и неотличимо. Реализовать план из раздела «Телеметрия для валидации»; минимум — `network_replaced_since_view`, `native_seen` и пиксель `background_restarted`. +- [ ] **Порядок выкатки.** Телеметрия раньше фиксов или вместе с ними в A/B-эксперименте с когортой в CPM-пикселях. +- [ ] **Error 6 на iOS** (~1/3 строк `stuck`) не покрыт фиксами. Следующий шаг — воспроизвести в harness (цепочка `dealloc → error:6 → view`). +- [ ] **Остаток «здоровых» сбоев без смены Network-процесса** (1 170 iOS / 497 macOS) не объяснён; разделить после `native_seen`. - [ ] **SPI.** Перезапуск через `_killWebContentProcessAndResetState`, детекция через `_networkProcessIdentifier`; есть `responds(to:)`. Согласовать с командой (приложение уже использует SPI WebKit в этом месте). - [ ] **Сборка приложений.** iOS и macOS не собирал; изменения обратно совместимы (дефолтная реализация нового метода протокола, дефолтный параметр init). Проверить сборку и прогнать на iOS Simulator. - [ ] **Zero-loss для смерти worker'а.** Нужен ping-канал в extension (native port с heartbeat или обработчик ping) — задача в репозитории extension.