--- title: CPM — что нужно в данных (требования к телеметрии из гипотез) date: '2026-10-15' tags: - work - cpm - 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 | | `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) ```