[2026-10-15] eagle: work/projects/cpm-status-2026-10-15.md work/projects/cpm-telemetry-data-requirements-2026-10-15.md
This commit is contained in:
1 parent
f1828051d0
commit
79f0e3db17
2 files changed
+168
-116
No files matched your search
@@ -0,0 +1,165 @@
|
||||
---
|
||||
title: CPM Web Extension — единый статус (PR, данные, гипотезы, телеметрия)
|
||||
date: '2026-10-15'
|
||||
tags:
|
||||
- work
|
||||
- cpm
|
||||
- status
|
||||
---
|
||||
|
||||
# CPM Web Extension: единый статус на 15 октября 2026
|
||||
|
||||
Внутренний документ. **Цифры из ClickHouse, в публичные PR, комментарии и issues не переносить** (репозитории `duckduckgo/apple-browsers` и `duckduckgo/duckduckgo-privacy-extension` публичные). Заменяет заметку `cpm-telemetry-data-requirements-2026-10-15` (теперь ссылка сюда). Старые документы с устаревшими числами: [[cpm-web-extension-public-attribution-2026-10-05]], [[cpm-web-extension-data-analysis-summary-2026-10-08]], [[cpm-web-extension-repro-and-fix-2026-10-08]], [[cpm-web-extension-code-guide-2026-10-09]] (карта «большого» worktree, **больше не актуальна**: всё нарезано на PR ниже).
|
||||
|
||||
## 1. Коротко
|
||||
|
||||
- Фикс **B** (падение Network-процесса) проверен локально и красным прогоном на CI. **Основание в данных есть, но неполное:** обогащение в stuck есть, а доказать, что строки это именно мёртвый worker, нельзя (не хватает `network_replaced_since_view`).
|
||||
- Фикс **A** (первая загрузка посреди сессии) реален, но мелкий: около 1% stuck на iOS, на macOS почти не виден, чаще самоизлечивается.
|
||||
- Три самые крупные группы сбоев фиксами A и B **не закрыты**: пробуждение после idle, error 6 (iOS), «нет background view» на запуске (iOS). Механизмы воспроизведены локально на macOS, но проверенного рычага в приложении нет.
|
||||
- Телеметрия PR0 пока в виде, вызвавшем вопросы о склейке пикселей. Решение по правкам не принято (раздел 8).
|
||||
|
||||
## 2. Цепочки PR (все в `duckduckgo/apple-browsers`, задачи Asana в телах PR; не читать)
|
||||
|
||||
**Телеметрия:**
|
||||
|
||||
| PR | Ветка | База | Что | Статус |
|
||||
|---|---|---|---|---|
|
||||
| #7376 (PR0) | `alex/cpm-messaging-readiness-telemetry` | main | параметры готовности и тайминга на failure/recovery пикселях (без новых пикселей) | CI зелёный, нужен ревью. Ревьюер по пикселям задал вопросы JA1–JA3 о склейке, ответ не отправлен |
|
||||
| #7384 (0b) | `alex/cpm-readiness-baseline` | #7376 | ежедневные базлайн-пиксели `cpm_background_ready_{context_load,wake}`, нагрузка устройства, возраст возобновления | CI в основном зелёный |
|
||||
| #7385 (0c) | `alex/cpm-init-progress-native` | #7384 | `init_stage` на failure-пикселях (нужен JS из privacy-extension#3727) | CI в основном зелёный |
|
||||
| privacy-extension #3727 | `alex/cpm-init-progress` | | уведомления `cpmInitProgress` из встроенного расширения | открыт, нужен ревью |
|
||||
|
||||
**Harness и фиксы** (красный коммит, затем зелёный; не ребейзить и не форс-пушить, иначе пропадёт доказательство красного прогона):
|
||||
|
||||
| PR | Ветка | База | Что | Статус |
|
||||
|---|---|---|---|---|
|
||||
| #7378 | `alex/cpm-repro-harness` | main | harness (только тесты); 4 быстрых теста в CI около 3 с, остальное локально через `CPM_REPRO=1` | CI зелёный, нужен ревью. Danger: 1570 добавленных строк (предупреждение). В ветке есть коммит другой сессии «Require initResp in the page…» |
|
||||
| #7379 | `alex/cpm-fix-init-loss` | harness | фикс A: ожидание первой загрузки background после установки/апгрейда посреди сессии | красный прогон и зелёный: CI зелёный |
|
||||
| #7381 | `alex/cpm-fix-network-crash` | фикс A | фикс B: перезапуск background при замене Network-процесса и мёртвом worker'е | **красный прогон подтверждён** (падает `testNetworkProcessCrashKeepsEveryTabWorking`, 3 ретрая, Non-Sandbox). Фикс-коммит `f1085846e7` **локально, не запушен** |
|
||||
| (нет PR) | `alex/cpm-fix-pixels` | фикс B + PR0 | пиксели `background_restart` и `first_load_wait` для измерения эффекта | держим до слияния PR0, A, B |
|
||||
|
||||
Локальные проверки фикса A: на коде без фикса оба repro-теста красные (5 из 5 итераций), с фиксом 0 из 10 потерь, 761 тест пакета зелёный. Фикс B: на `main` красные тесты падения Network-процесса и смерти worker'а, 5 из 5 итераций.
|
||||
|
||||
Недоделано в harness и CI: CI гоняет тесты с `-test-iterations 3 -retry-tests-on-failure`, красный тест должен падать стабильно; тесты WebExtensions идут внутри схем `macOS Browser` и `iOS Browser` на `osx-tahoe-27`. Медленные эксперименты (заморозка 45 с, заполнение кэша около 10 минут, медленный старт worker'а) локальные и не для коммита.
|
||||
|
||||
## 3. Данные (ClickHouse, 12–14 октября, версии iOS 7.240+ и macOS 1.210+)
|
||||
|
||||
Единица: `daily`-пиксель = устройство-день на причину, не пользователь и не эпизод. `initialization_failed` только `.daily`; `stuck`, `recovered_*`, `reload_failed`: `daily`+`count`. Параметры снимка есть только на init_failed и stuck.
|
||||
|
||||
| | iOS | macOS |
|
||||
|---|---|---|
|
||||
| `initialization_failed.daily` | 3 273 025 | 119 950 |
|
||||
| `stuck.daily` | 511 660 | 40 842 |
|
||||
| init восстановились без reload | 1 135 477 (34.7%) | 30 738 (25.6%) |
|
||||
| stuck → восстановились после M4 reload | 293 557 (57.4%) | 21 865 (53.5%) |
|
||||
| `reload_failed.daily` | 140 921 (27.5% от stuck) | 17 796 (43.6%) |
|
||||
|
||||
**Когорты** (по приоритету: error 6 → нет view → вкладка не подключена → Network заменён → пробуждение (view <30 с после `dealloc`) → недавняя загрузка (`load`/`base_url_changed` ≤2 мин) → other). Доля init → stuck:
|
||||
|
||||
| Когорта | iOS | macOS | Смысл роста или падения |
|
||||
|---|---|---|---|
|
||||
| error 6 | 6.1% → 21.5% | 1.0% → 1.9% | остаётся сломанной |
|
||||
| нет background view | 48.1% → 10.9% | 3.3% → 1.8% | самоизлечивается |
|
||||
| вкладка не подключена | 0.03% → 0.05% | 6.2% → 8.9% | остаётся сломанной |
|
||||
| Network заменён | 16.8% → 39.7% | 13.6% → 26.6% | остаётся сломанной |
|
||||
| пробуждение | 7.9% → 9.5% | 32.5% → 32.2% | без изменений |
|
||||
| недавняя загрузка контекста | 15.7% → 6.0% | 20.0% → 6.4% | самоизлечивается |
|
||||
| other | 5.3% → 12.5% | 23.4% → 22.2% | |
|
||||
|
||||
Объёмы `stuck.count` в разных версиях на порядки выше `daily`: дашборды сравнивать только по версии и частоте.
|
||||
|
||||
## 4. Основание фиксов
|
||||
|
||||
- **B:** доля «Network заменён» растёт в 2–2.4× при переходе init → stuck. `reload_failed` высок: reload расширения мёртвый worker не лечит. **Оговорка:** `network_restarted` сравнивает PID с загрузкой *контекста*, а не с последним view. Если view пересоздан после смены процесса, worker свежий и механизм неприменим. По заметке 8 октября механизм объясняет в лучшем случае 20–45% «здоровых» stuck. Причинность подтвердят `network_replaced_since_view` (нет ни в одной ветке) и `init_stage=none` (#7385).
|
||||
- **A:** сигнатура «загрузка контекста ≤2 мин, не при запуске, первый view»: iOS 129k init (3.9%) → 4.6k stuck (0.9%); macOS 505 → 118. Разовая потеря одной навигации (0.23× при переходе в stuck). Сигнатура неточна (захватывает M4-reload). Название связанной Asana-задачи («init loss on background unload») относится скорее к группе пробуждения, чем к фиксу A; несоответствие надо снять с автором задачи.
|
||||
|
||||
## 5. Картины событий (stuck.daily, 14 октября, хвост `bg_events` без возрастов) и воспроизведение
|
||||
|
||||
| Картина | Хвост | iOS / macOS | Воспроизведено? | Рычаг в приложении |
|
||||
|---|---|---|---|---|
|
||||
| **P1. Цикл пробуждения** | `view>proxy_on>dealloc>view>proxy_on` | 16.0k / 4.3k (wake) + 16.3k / 2.4k (старше 30 с) | только с искусственно занятым worker'ом (5 с: init_failed и поздний ответ; 12 с: stuck → M4); органическое пробуждение в тестах проходит. WebKit-баг 324743 (исправлен 22 сентября, нет в 26.7/27.0) | гипотезы: отсчёт 4 с от готовности background («фаза 2 детектора»), ожидание готовности при пробуждении, предпрогрев |
|
||||
| **P2. «Пробуждение → error 6»**, циклы | `proxy_on>dealloc>view>proxy_on>error_E:6` (23.6k), `error>dealloc>view>proxy_on>error` (5.7k), после `died_crash` (1.5k) | iOS; macOS 118 | macOS: заморозка нового процесса на ≥30 с даёт error 6, корзина 25–35 с. iPhone: 4 сценария безуспешно. Главный iOS-хвост **без** `unresponsive` (в воспроизведении он есть) | корень в WebKit (`wakeUpBackgroundContentIfNecessary` взводит 30-секундный таймер до загрузки worker'а). Гипотеза: перезагрузка расширения при возврате на передний план |
|
||||
| **P3. Контекст загружен, view нет** | `load` / `load>base_url_changed` | 17.1k + 0.9k / 162 + 81 | задержка очистки при смене `baseURL` воспроизведена на macOS при большом кэше (около 10 минут заполнения); iOS-гонка запуска (`bg_view_creations=0`, восстановление сессии, первые 30 с) не воспроизведена: холодный старт в harness'е отвечает за миллисекунды | кандидат: стабильный `baseURL` (ветка `cpm-stable-base-url`: не доведён, меняет URL всем расширениям, не прогнан через harness, старая база). Фикс WebKit 318756@main, номер сборки ОС не проверен |
|
||||
| **P4. Первая загрузка** | `load>view>proxy_on` | 6.8k + 2.3k / 570 | да (фикс A) | фикс A |
|
||||
| **P5. Фон выгружен в момент сбоя** | `proxy_on>dealloc>view>proxy_on>dealloc` | 1.9k / 494 | нет | нет гипотезы |
|
||||
| **P6. macOS: вкладка без расширения** | `load>view>proxy_on>dealloc`, с `extension_updated` | iOS ≈0 / 627 + 235 | нет сценария | нет; фиксы привязку вкладки не трогают |
|
||||
| **Network заменён** | | см. раздел 4 | да (краш, worker мёртв при том же view) | фикс B |
|
||||
| **«Здоровый» остаток, other** | | iOS 12.5%, macOS 22% | нет | нет гипотезы |
|
||||
|
||||
Размер группы не равен возможности её починить: P1 и P3 в основном опаздывающие ответы, а часть «сбоев» это срабатывание детектора («нет ответа за 4 с», опоздавший ответ тоже считается). iOS-когорта «other» на самом деле тот же рисунок P1, но со view старше 30 секунд.
|
||||
|
||||
## 6. Телеметрия
|
||||
|
||||
**Что делает PR0 (#7376):** на 8 failure-пикселей и 2 recovery-пикселя добавлены параметры `bg_ready_after`, `bg_view_age`, `bg_load_error_after`, `bg_load_error_app_state`, `cpm_app_state`, `cpm_open_tabs`, `cpm_loading_tabs` (failure) и `cpm_recovery_document`, `cpm_recovery_after` (recovery). Новых пикселей нет. Recovery-пиксели получают только свои два параметра, `extension_reload_failed` без параметров.
|
||||
|
||||
**Замечания ревьюера (JA1–JA3):** корреляция между пикселями из-за множества перечислений; сколько пикселей на ошибку; константность `cpm_open_tabs`/`cpm_app_state`; что такое background view.
|
||||
|
||||
**Что верно по пикселям (исправление моей прежней ошибки):** `initialization_failed` только daily (не больше одного на устройство в день на причину); `stuck` daily+count (daily и count одного события идут с одинаковыми параметрами); snapshot пересобирается при каждой отправке, поэтому быстро меняющиеся поля (`bg_view_age`, `bg_ready_after`) между init_failed и stuck различаются, медленные (`app_state`, `open_tabs`) повторяются.
|
||||
|
||||
**Уникальность строк `stuck.daily` внутри дня** (доля уникальных по всем параметрам):
|
||||
|
||||
| Вариант | iOS | macOS |
|
||||
|---|---|---|
|
||||
| сейчас, `bg_events` из 12 событий | 8.3% | 30% |
|
||||
| информативные токены, последние 4 | 2.1% | 5.2% |
|
||||
| скаляры вместо `bg_events` | 0.96% | 4.4% |
|
||||
| без `bg_events` | 0.25% | 2.0% |
|
||||
|
||||
Верхняя граница прироста от новых полей (синтетическое независимое равновероятное поле): со старым `bg_events` при десяти значениях iOS 21%, macOS 65%; со скалярами 3.6% / 15.8%. **Вывод:** «хуже не будет» можно утверждать только со скалярами. Главный идентификатор уже выкачен (`bg_events`).
|
||||
|
||||
**`bg_events`:** единственный источник давности загрузки (`load`, `base_url_changed`) и причины смерти (`died_*`). `view`, `dealloc`, `proxy_on`, `error_*` дублируют `bg_view_creations` и `context_errors`; `hold/release/proxy_off` равны нулю. Скаляры воспроизводят когорты загрузки и пробуждения на 100% строк. Теряется: поиск неожиданных последовательностей в остатке (смягчить удалённым флагом).
|
||||
|
||||
**Минимум данных по гипотезам:**
|
||||
|
||||
| Поле / пиксель | Вопрос | Где |
|
||||
|---|---|---|
|
||||
| `bg_ready_after`, `bg_view_age` | идёт ли загрузка, пробуждение или давно | PR0 |
|
||||
| `bg_load_error_after`, `bg_load_error_app_state` (только при error 6) | таймер 30 с, состояние приложения | PR0 |
|
||||
| `recovery_document`, `recovery_after` | опоздавший ответ или потерянный | PR0 |
|
||||
| `app_state`, `open_tabs` (грубо, 1 / 2–5 / >5), только на `initialization_failed` | когорта «нет view» на запуске | PR0 |
|
||||
| **новое:** `context_load_cause`, `context_load_age` (вместо `load@` и `extension_updated`) | точная сигнатура A | PR0 |
|
||||
| **новое:** `bg_died_reason`, `base_url_changed` | смерть процесса, очистка при смене baseURL | PR0 |
|
||||
| **новое:** мелкие корзины `app_launch_age` для первых 30 с (<4 / 4–10 / 10–30) | гонка запуска | PR0 |
|
||||
| **новое на stuck:** `stuck_after` | сколько от первого сбоя до stuck | PR0 |
|
||||
| **новое, срочно:** `network_replaced_since_view` | доля реальных мёртвых worker'ов в когорте B (рекордер вычисляет, но только логирует; предложено 8 октября, **нигде не реализовано**) | PR0 |
|
||||
| `init_stage` | дошёл ли init до background | #7385 (срочно) |
|
||||
| `cpm_background_ready_{context_load,wake}` | базлайн здоровых загрузок и пробуждений | #7384 |
|
||||
| `background_restart` (причина, исход; **убрать из него `bg_ready_after`**, он дублирует соседний failure-пиксель), `first_load_wait` | эффект B и A | удержанная ветка |
|
||||
| убрать | `loading_tabs`; из таймлайна `view`, `dealloc`, `proxy_on`, `error_*`, `hold/release/proxy_off` | |
|
||||
|
||||
Связность стека: #7384 добавляет ещё 5 измерений на те же 8 failure-пикселей (`context_load_to_background_view`, `cpm_low_power_mode`, `cpm_thermal_state`, `cpm_lockdown_mode`, `cpm_resume_age`); оценку склейки надо повторять и по нему. `background_restart` срабатывает вместе с первым `initialization_failed`: общий ключ `bg_ready_after`.
|
||||
|
||||
## 7. Критерии успеха и опровержения
|
||||
|
||||
- **B** подтверждается, если после релиза падают доля «Network заменён» среди stuck и `reload_failed`, `background_restart` показывает восстановление, а у таких stuck `init_stage=none`.
|
||||
- **A** подтверждается, если сокращается когорта «мид-сессионная первая загрузка» (`context_load_cause`). Если нет, основание слабое.
|
||||
- Остаток P1/P3: гипотеза «медленный старт, а не потеря» подтверждается, если `bg_ready_after` у упавших заметно хуже базлайна здоровых загрузок и `recovery_document=failed_navigation`.
|
||||
|
||||
## 8. Открытые решения и следующие шаги
|
||||
|
||||
1. **Правки PR0** (ждут вашего «да»): скаляры вместо `bg_events`, `context_load_cause`, `stuck_after`, `network_replaced_since_view`, мелкие корзины `app_launch_age`, убрать `loading_tabs`, константы только на `initialization_failed`, `open_tabs` грубо, `bg_load_error_*` только при error 6; после этого ребейз #7384 и #7385. Альтернатива «оставить `bg_events`» требует ответа ревьюеру без утверждения «хуже не будет» и обязательства перемерить после релиза.
|
||||
2. **Ответ ревьюеру** (публично, **без цифр**): JA1 (init_failed daily-only; stuck daily+count с одинаковыми параметрами; recovered несут только `recovery_*`), JA2 (медленные поля повторяются, быстрые различаются; планируется сократить набор), JA3 (background view = скрытый `WKWebView`, в котором WebKit запускает фоновый скрипт расширения; выгружается через около 30 с простоя и пересоздаётся при пробуждении). Обязательство: перемерить различимость строк после первого релиза.
|
||||
3. **Запушить фикс B** (`f1085846e7`): красный прогон подтверждён, ждёт зелёного.
|
||||
4. **Судьба фикса A:** слабое основание в данных, не совпадает с названием задачи; решить, сливать ли без зелёного измерения эффекта через `first_load_wait`.
|
||||
5. **P3:** довести стабильный `baseURL` (ограничить встроенными расширениями без `web_accessible_resources`, прогнать harness, перенести на свежий `main`), проверить номер сборки ОС с фиксом 318756@main.
|
||||
6. **Локальные repro для P1–P3** отдельным PR без фиксов (по образцу harness'а, `requireLocalRun`), чтобы гонять после релизов WebKit.
|
||||
7. **iOS:** воспроизведение error 6 на реальном устройстве; сценарий «вкладка без расширения» на macOS.
|
||||
8. Блокеры по ревью: #7376 и #7378 ждут ревью; #7384/#7385 стоят на #7376.
|
||||
|
||||
## 9. Методика и инструменты
|
||||
|
||||
- Доступ: `~/Developer/grafana-data-tools` (Grafana → ClickHouse `metrics.pixels`, read-only; авторизация через `grafana-auth`). Ad-hoc SQL запускался отдельным раннером через `~/DuckDuckGo/grafana-dashboards/tools/grafana-query/read-dashboard.py`. Сканы по `pixel LIKE` без фильтра по дате, агенту и `pixel_id='debug'` превышают лимит строк. Временные запросы лежали в папке сессии и могут пропасть; ключевая классификация ниже.
|
||||
- Версии с диагностикой: iOS 7.240+, macOS 1.210+. Окно: 12–14 октября (3 полных дня). Ограничения: сигнатуры эвристические; `network_restarted` только прокси; устройства нельзя проследить от init до stuck, сравниваются только доли; доля не равна пользовательскому эффекту.
|
||||
|
||||
```sql
|
||||
-- классификация когорт для init_failed.daily / stuck.daily
|
||||
multiIf(
|
||||
errs LIKE '%ContextErrorDomain:6%', '1_error6',
|
||||
creations = '0', '2_no_background_view',
|
||||
scripts = 'false', '3_tab_not_connected',
|
||||
net = 'true', '4_network_replaced',
|
||||
has(ev,'view@30s') AND arrayExists(x -> startsWith(x,'dealloc@'), ev), '5_wake_view_lt30s',
|
||||
arrayExists(x -> x IN ('load@30s','load@1m','load@2m','base_url_changed@30s','base_url_changed@1m','base_url_changed@2m'), ev), '6_context_load_recent',
|
||||
'7_other')
|
||||
-- параметры: network_restarted, context_errors, bg_view_creations, tab_has_ext_scripts, bg_events (decodeURLComponent)
|
||||
```
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: CPM — что нужно в данных (требования к телеметрии из гипотез)
|
||||
title: CPM — что нужно в данных (перенесено)
|
||||
date: '2026-10-15'
|
||||
tags:
|
||||
- work
|
||||
@@ -7,119 +7,6 @@ tags:
|
||||
- telemetry
|
||||
---
|
||||
|
||||
# CPM: что нужно в данных
|
||||
# Перенесено
|
||||
|
||||
Внутренняя заметка. **Цифры из ClickHouse, в публичные PR/комментарии не переносить.** Контекст: [[cpm-readiness-telemetry-tech-design-2026-10-15]], [[cpm-web-extension-data-analysis-summary-2026-10-08]], [[cpm-web-extension-code-guide-2026-10-09]]. PR: telemetry #7376 → #7384 → #7385; harness #7378 → fix A #7379 → fix B #7381.
|
||||
|
||||
## Метод
|
||||
|
||||
- Окно: 2026-10-12…14 UTC (3 полных дня), версии с диагностикой: iOS 7.240+, macOS 1.210+. Единица: `daily`-пиксель = устройство-день на причину, не пользователь и не эпизод.
|
||||
- Инструмент: `~/Developer/grafana-data-tools` (`query.py`, Grafana → ClickHouse `metrics.pixels`, read-only). Запросы ниже запускались через тот же helper (`read-dashboard.py`) отдельным раннером; SQL приведён в конце.
|
||||
- Классификация когорт по приоритету: error 6 → нет background view → tab not connected → Network заменён → пробуждение (view <30 с после `dealloc`) → недавняя загрузка контекста (`load`/`base_url_changed` ≤2 мин) → other.
|
||||
|
||||
## Объёмы (12–14 октября)
|
||||
|
||||
| | iOS | macOS |
|
||||
|---|---|---|
|
||||
| `initialization_failed.daily` | 3 273 025 | 119 950 |
|
||||
| `stuck.daily` | 511 660 | 40 842 |
|
||||
| восстановились без reload (daily, `from=initialization_failed`) | 1 135 477 (34.7% от init) | 30 738 (25.6%) |
|
||||
| stuck → восстановились после M4 reload (daily) | 293 557 (57.4% от stuck) | 21 865 (53.5%) |
|
||||
| `reload_failed.daily` | 140 921 (27.5% от stuck) | 17 796 (43.6%) |
|
||||
|
||||
`initialization_failed` — только `.daily`; `stuck`, `recovered_*`, `reload_failed` — `daily`+`count`. Параметры снимка есть только на init_failed и stuck; на recovered только `recovery_from`/`recovery_document`/`recovery_after`.
|
||||
|
||||
## Когорты: доля init → stuck
|
||||
|
||||
| Когорта | iOS init → stuck | macOS init → stuck |
|
||||
|---|---|---|
|
||||
| error 6 | 6.1% → 21.5% | 1.0% → 1.9% |
|
||||
| нет background view | 48.1% → 10.9% | 3.3% → 1.8% |
|
||||
| tab not connected | 0.03% → 0.05% | 6.2% → 8.9% |
|
||||
| Network заменён | 16.8% → 39.7% | 13.6% → 26.6% |
|
||||
| пробуждение (view <30 с) | 7.9% → 9.5% | 32.5% → 32.2% |
|
||||
| недавняя загрузка контекста | 15.7% → 6.0% | 20.0% → 6.4% |
|
||||
| other | 5.3% → 12.5% | 23.4% → 22.2% |
|
||||
|
||||
Рост доли при переходе init → stuck значит, что когорта чаще остаётся сломанной (error 6, Network заменён, tab not connected); падение значит самоизлечение (нет view, недавняя загрузка).
|
||||
|
||||
## Основание для фиксов
|
||||
|
||||
- **B (Network-процесс)**: когорта «Network заменён» растёт в 2–2.4× при переходе в stuck; `reload_failed` высок (reload расширения не лечит мёртвый worker). Основание сильное. Ограничение: `network_restarted` это прокси «PID изменился»; причинность подтверждает только `init_stage=none` (#7385).
|
||||
- **A (первая загрузка посреди сессии)**: сигнатура «загрузка ≤2 мин, не при запуске (launch age ≥15 мин), первый view»: iOS 129k init (3.9%) → 4.6k stuck (0.9%); macOS 505 → 118. Реально, но разово (0.23×) и на macOS мелко. Сигнатура неточна: захватывает и M4-reload; нужен `context_load_cause`.
|
||||
|
||||
## Паттерны событий без фиксов (stuck.daily, 14 октября, хвост `bg_events` без возрастов)
|
||||
|
||||
| Картина | Хвост | iOS / macOS (строк) | Чем различать |
|
||||
|---|---|---|---|
|
||||
| **P1. Цикл пробуждения**: idle-выгрузка, затем view | `view>proxy_on>dealloc>view>proxy_on` | 16.0k / 4.3k (wake) + 16.3k / 2.4k (то же, но view старше 30 с, класс other) | медленный старт или потеря: `bg_ready_after`, `bg_view_age`, `init_stage`, `recovery_after`; базлайн здоровых пробуждений (#7384) |
|
||||
| **P2. Цикл «пробуждение → error 6»** | `proxy_on>dealloc>view>proxy_on>error_E:6` (23.6k), повторяется `error>dealloc>view>proxy_on>error` (5.7k), после `died_crash` (1.5k) | iOS; macOS 118 | `bg_load_error_after` (бакет 25–35 с = таймер 30 с), `bg_load_error_app_state`, `bg_view_creations` |
|
||||
| **P3. Контекст загружен, view не создан** | `load` или `load>base_url_changed` | 17.1k + 0.9k / 162 + 81 | `bg_ready_after=none`, `context_load_age`, мелкие корзины `app_launch_age`, `app_state`, время загрузка→view из #7384 |
|
||||
| **P4. Первая загрузка: `load>view>proxy_on`** | | 6.8k + 2.3k / 570 | `context_load_cause`, `bg_view_creations=1`; эффект A измерять `first_load_wait` |
|
||||
| **P5. Фон выгружен в момент сбоя** (хвост на `dealloc`) | `proxy_on>dealloc>view>proxy_on>dealloc` | 1.9k / 494 | `bg_view_alive=false` (уже есть), `bg_ready_after`, `bg_view_age` |
|
||||
| **P6. macOS: вкладка без расширения** | `load>view>proxy_on>dealloc`, часто с `extension_updated` | iOS ≈0 / 627 + 235 | есть `tab_controller_match`, `tab_has_ext_scripts`, `app_version_change`, `reason` |
|
||||
|
||||
В iOS-когорте «нет view» крупнейшие группы лежат в первых 30 с после запуска: восстановление сессии (около половины) и `reason=other`. Первая корзина `app_launch_age` «<30 с» слишком грубая.
|
||||
|
||||
## Что нужно в данных (минимум)
|
||||
|
||||
| Поле / пиксель | Вопрос | Где |
|
||||
|---|---|---|
|
||||
| `bg_ready_after` | всё ещё загружается, загрузился или упал | PR0 |
|
||||
| `bg_view_age` | пробуждение или давно | PR0 |
|
||||
| `bg_load_error_after`, `bg_load_error_app_state` (только при error 6) | таймер 30 с, состояние приложения | PR0 |
|
||||
| `recovery_document`, `recovery_after` | опоздавший ответ или потерянный | PR0 |
|
||||
| `app_state`, `open_tabs` (грубо), только на `initialization_failed` | когорта «нет view» на запуске | PR0 |
|
||||
| **новое:** `context_load_cause` (запуск / установка / апгрейд / reload при восстановлении) и `context_load_age` | точная сигнатура A, замена `load@` и `extension_updated` | PR0 |
|
||||
| **новое:** `bg_died_reason`, `base_url_changed` | смерть процесса, очистка при смене baseURL | PR0 |
|
||||
| **новое:** мелкие корзины `app_launch_age` для первых 30 с | гонка на запуске | PR0 |
|
||||
| **новое на stuck:** `stuck_after` | сколько от первого сбоя до stuck | PR0 |
|
||||
| **новое, срочно:** `network_replaced_since_view` (PID Network-процесса при последнем создании view ≠ текущему) | `network_restarted` сравнивает PID с загрузкой *контекста*, не с последним view: порядок восстановить нельзя, поэтому доля реальных «мёртвых worker'ов» в когорте B неизвестна. Рекордер уже вычисляет это (`CPMMessagingDiagnosticsRecorder.swift:285-289`), но только логирует. Предложено 8 октября как «самое дешёвое и самое ценное», **ни в одной ветке не реализовано** | PR0 |
|
||||
| `init_stage` | дошёл ли init до background | #7385 (срочно) |
|
||||
| `cpm_background_ready_{context_load,wake}` | базлайн здоровых загрузок и пробуждений | #7384 |
|
||||
| `background_restart` (причина, исход), `first_load_wait` | эффект фиксов B и A | удержанная ветка |
|
||||
| убрать | `loading_tabs`; в таймлайне `view`, `dealloc`, `proxy_on`, `error_*`, `hold/release/proxy_off` | |
|
||||
|
||||
### `bg_events`
|
||||
|
||||
- Сейчас единственный источник давности загрузки и причины смерти процесса. Остальные токены дублируют `bg_view_creations`, `context_errors` или нули (`hold/release/proxy_off` = 0%).
|
||||
- Скаляры вместо таймлайна воспроизводят когорты загрузки и пробуждения на 100% строк (измерено 14 октября).
|
||||
- Однодневная уникальность строк `stuck.daily` по всем параметрам: с `bg_events` 8.3% (iOS) и 30% (macOS); сокращённые информативные токены 2.1% / 5.2%; скаляры 0.96% / 4.4%; без событий 0.25% / 2.0%.
|
||||
- Верхняя граница прироста от новых полей (синтетическое независимое равновероятное поле): с текущим `bg_events` при десяти значениях iOS 21%, macOS 65%; со скалярами 3.6% / 15.8%. Про «не станет хуже» можно говорить только со скалярами. В публичный PR цифры не писать.
|
||||
|
||||
## Критерии успеха и опровержения
|
||||
|
||||
- **B** подтверждается, если после релиза падают доля «Network заменён» среди stuck и `reload_failed`, `background_restart` показывает восстановление, а у таких stuck `init_stage=none`.
|
||||
- **A** подтверждается, если сокращается когорта «мид-сессионная первая загрузка» (`context_load_cause`). Если нет, основание слабое: она около 1% stuck на iOS и почти 0 на macOS.
|
||||
- Остаток P1/P3 подтверждает гипотезу «медленный старт, а не потеря», если `bg_ready_after` у упавших заметно хуже базлайна здоровых загрузок и `recovery_document=failed_navigation`.
|
||||
|
||||
## SQL (классификация; даты подставлять)
|
||||
|
||||
```sql
|
||||
-- stuck.daily / init_failed.daily; приоритет когорт
|
||||
multiIf(
|
||||
errs LIKE '%ContextErrorDomain:6%', '1_error6',
|
||||
creations = '0', '2_no_background_view',
|
||||
scripts = 'false', '3_tab_not_connected',
|
||||
net = 'true', '4_network_replaced',
|
||||
has(ev,'view@30s') AND arrayExists(x -> startsWith(x,'dealloc@'), ev), '5_wake_view_lt30s',
|
||||
arrayExists(x -> x IN ('load@30s','load@1m','load@2m','base_url_changed@30s','base_url_changed@1m','base_url_changed@2m'), ev), '6_context_load_recent',
|
||||
'7_other')
|
||||
-- параметры: network_restarted, context_errors, bg_view_creations, tab_has_ext_scripts, bg_events (decodeURLComponent)
|
||||
```
|
||||
|
||||
## Картины из данных, которые НЕ воспроизведены (сверка с тестами)
|
||||
|
||||
Воспроизведено локально (worktree `alex/cpm-web-extension-remaining-failures` и harness): падение Network-процесса и смерть worker'а (красные на `main`, 5/5 итераций), мид-сессионная первая загрузка (A), error 6 через заморозку нового процесса background на ≥30 с при пробуждении (macOS, корзина 25–35 с), задержка инъекции из-за очистки при смене `baseURL` (macOS, большой кэш, около 10 минут заполнения), медленный старт worker'а на 5 с и 12 с (инъекция занятости).
|
||||
|
||||
Не воспроизведено или воспроизведено только частично:
|
||||
|
||||
| Картина | Статус | Пробел |
|
||||
|---|---|---|
|
||||
| Когорта «Network заменён» | механизм воспроизведён (краш, worker мёртв при том же view) | по данным нельзя сказать, какая доля строк это он: `network_restarted` сравнивается с загрузкой контекста. Заметка 8 октября: механизм объясняет 20–45% «здоровых» stuck |
|
||||
| iOS error 6, главная последовательность `proxy_on>dealloc>view>proxy_on>error_E:6` без `unresponsive`, циклы `error>dealloc>view>proxy_on>error`, `died_crash → error 6` | на macOS воспроизведено заморозкой (цепочка там содержит `unresponsive`); на iPhone 26.7.1 в четырёх сценариях нет; убийство background во время загрузки в harness error 6 не даёт | реальный триггер на iOS неизвестен |
|
||||
| Пробуждение (P1) | только с искусственной занятостью worker'а; органическое пробуждение в тестах проходит | причина реальной медлительности неизвестна |
|
||||
| P5: фон выгружен в момент сбоя (хвост на `dealloc`) | не воспроизведено | неясно, как WebKit выгружает новый view повторно до прихода сообщения |
|
||||
| iOS «нет view» на запуске (восстановление сессии, `load` без view) | не воспроизведено: холодный старт в harness отвечает за миллисекунды; задержка очистки есть только при большом кэше на macOS | гонка запуска на iOS не проверена |
|
||||
| macOS: вкладка без расширения (неверный controller, нет scripts) | не воспроизведено, сценария нет | фиксы привязку вкладки не трогают |
|
||||
| «Здоровый» остаток без смены Network (iOS 12.5%, macOS 22%, и часть пробуждений со старым view) | нет гипотезы | `init_stage`, `bg_view_age` |
|
||||
Содержимое этой заметки (требования к телеметрии из гипотез, когорты ClickHouse, паттерны событий, список невоспроизведённых картин, анализ `bg_events`, SQL) сведено в единый документ со статусом: [[cpm-status-2026-10-15]].
|
||||
Reference in new issue
Block a user