129 lines
37 KiB
Markdown
Executable File
129 lines
37 KiB
Markdown
Executable File
# CPM Web Extension: attribution и сравнение релизов — 5 октября 2026
|
||
|
||
**Статус:** первый завершённый UTC-день с данными версий iOS 7.240.0 и macOS 1.210.0. Главный сигнал — `initialization.failed.after.*.daily`; `messaging.stuck.*.daily` проверен отдельно как более поздний, менее точный сигнал. Выгрузка сделана 6 октября из Grafana ClickHouse `metrics.pixels`, интервал `[2026-10-05 00:00, 2026-10-06 00:00) UTC`, `agent` и `appVersion` зафиксированы. **Основные таблицы ниже включают только события после публикации release tag каждой платформы**; отдельный раздел сохраняет всё попавшее в выгрузку до публикации. [Dashboard](https://grafana.duckduckgo.com/d/dfon7whlilnggb/cpm-weird-error-investigation?orgId=1).
|
||
|
||
**Вывод:** после публикации macOS tag есть **77** первых init failures и **35** stuck с несовпадающим controller вкладки и отсутствующими extension scripts; **до публикации таких строк нет**. iOS session restoration по-прежнему даёт 952 первых сбоя, из них 604 со совпадающим controller, но без scripts и background view; у macOS после публикации таких session-restoration сбоев 68, из них 12 с той же сигнатурой. **Крупнейшая группа с «исправным native-снимком» не доказывает нормальную загрузку или потерю `initResp`: health monitor измеряет отдельный вызов background → native `refreshCpmDashboardState`.** После reload этот вызов вновь наблюдается гораздо чаще, но восстановление фактического `initResp` из пикселей не следует. Эффект исправлений tab lifecycle в совокупном тренде не выделяется.
|
||
|
||
## 1. Охват и контрольные суммы
|
||
|
||
| После release tag: независимые имена `.daily` пикселей | iOS 7.240 | macOS 1.210 |
|
||
|---|---:|---:|
|
||
| Первое `initialization.failed.after.*` | **6 624** | **1 377** |
|
||
| Позднее `messaging.stuck.*` | 1 440 | 487 |
|
||
| Первый неудачный post-reload CPM probe | 670 | 232 |
|
||
| CPM ответ после reload, оба `recovery_from` | 963 = 916 stuck + 47 init | 362 = 354 stuck + 8 init |
|
||
| CPM ответ без reload, `initialization_failed` | 1 555 | 346 |
|
||
| **Сумма записей CPM daily по именам пикселей** | **11 252** | **2 804** |
|
||
|
||
Контроль после release tag: `6 624 + 1 377 = 8 001` первичных строк; `1 440 + 487 = 1 927` вторичных строк; `11 252 + 2 804 = 14 056` CPM daily записей по именам пикселей. До tag есть ещё 28 init, 2 stuck и 39 CPM daily записей, все на macOS; полные файлы поэтому содержат `8 029` init, `1 929` stuck и `14 095` CPM daily. Разделение по времени и pixel name воспроизводит `release_cutover_daily.tsv`, полный день сходится с `inventory.tsv` и `release_cpm_daily_trends.tsv`. Включать `debug.web.extension.reload.failed.daily` в сумму CPM нельзя: это отдельное семейство операций Web Extensions. Внутри CPM `initialization.failed.after.extension.reload.daily` и `messaging.extension.reload.failed.daily` обычно посылаются вместе на одной неудачной post-reload проверке: их суммы не обозначают разные эпизоды. `.daily` ограничен одним событием **на имя пикселя** за UTC-день на установку; разные имена могут относиться к одному пользователю и эпизоду. Переход версии внутри дня тоже меняет наблюдаемые суммы. Здесь нет числа уникальных пользователей, DAU той же версии и идентификатора для связывания событий.
|
||
|
||
Публикация release tag состоялась 5 октября в 15:18 UTC для [iOS 7.240](https://github.com/duckduckgo/apple-browsers/releases/tag/7.240.0%2Bios) и 09:46 UTC для [macOS 1.210](https://github.com/duckduckgo/apple-browsers/releases/tag/1.210.0%2Bmacos). Время после tag — доступная граница, **не доказательство public-канала у каждого отправителя**: пиксель не содержит channel/rollout cohort, и internal testing могло продолжаться после публикации.
|
||
|
||
### До публикации release tag: отдельный срез
|
||
|
||
В пределах выгрузки за 5 октября до публикации iOS tag нет CPM daily событий версии 7.240. До публикации macOS tag у версии 1.210 есть **28 init failure.daily, 2 stuck.daily, 2 post-reload failed.daily, 7 recovered.without.reload.daily, 0 recovered.after.reload.daily** — всего **39 CPM daily**. В этих 28 init: 12 native snapshot без видимой ошибки, 7 background state, 5 только context error history, 3 tab attachment/injection (2 без scripts/zero views и 1 tab-not-in-context), 1 с неполными tab facts. В двух stuck нет отсутствующих scripts или controller mismatch. `debug.web.extension.reload.failed.daily` до tag нет. Более ранние internal-testing данные 28 сентября – 4 октября сохранены в [[cpm-web-extension-breakage-validation]] как архив и **не смешиваются** с таблицами после публикации.
|
||
|
||
## 2. Первичный сигнал: группы и события рядом с `init failure.daily`
|
||
|
||
Каждая строка попадает ровно в одну диагностическую группу по приоритету: tab scripts/controller → явные признаки background → история ошибок → неполные поля → видимо исправный native snapshot. Это **классификация снимка**, а не доказательство первопричины. Полные пересекающиеся признаки сохранены отдельно.
|
||
|
||
| После release tag: диагностическая группа | iOS init, из 6 624 | macOS init, из 1 377 | iOS stuck, из 1 440 | macOS stuck, из 487 |
|
||
|---|---:|---:|---:|---:|
|
||
| Native snapshot без видимой ошибки | 3 717 (56,1%) | 911 (66,2%) | 789 (54,8%) | 335 (68,8%) |
|
||
| Tab attachment / injection | 931 (14,1%) | 116 (8,4%) | 53 (3,7%) | 38 (7,8%) |
|
||
| Background state | 768 (11,6%) | 158 (11,5%) | 457 (31,7%) | 35 (7,2%) |
|
||
| Только история ошибок context | 343 (5,2%) | 158 (11,5%) | 87 (6,0%) | 79 (16,2%) |
|
||
| Недостаточно tab facts | 865 (13,1%) | 34 (2,5%) | 54 (3,8%) | 0 |
|
||
|
||
Контроль групп после tag: iOS init `3717+931+768+343+865=6624`; macOS init `911+116+158+158+34=1377`; iOS stuck `789+53+457+87+54=1440`; macOS stuck `335+38+35+79=487`. Контроль причин первого сбоя: iOS `other 4803 + session.restoration 952 + extension.reload 670 + tab.crash 199 = 6624`; macOS `1050+68+232+27=1377`. Это `reason` навигации/эпизода, **не причина поломки**.
|
||
|
||
**Окружающие события по главным цепочкам:**
|
||
|
||
1. **iOS session restoration, 952.** У 604 (63,4%) `tab_controller_match=true`, scripts отсутствуют, `bg_view_creations=0`; у 607 хвост `bg_events` содержит только `load`, ещё у 283 — `load → view → proxy_on`. 754 первых сбоя попали в bucket первых 30 секунд запуска. Код ожидает начальный `loadAndSync` и загрузку background content перед первой HTTP(S)-навигацией restored tab, но общий дедлайн ожидания — 5 секунд; неоконченная загрузка и более позднее внедрение script всё ещё способны дать временный timeout. 260 снимков уже выглядят исправно и требуют timings сообщения, а не ещё одного снимка состояния.
|
||
2. **iOS `other`, 4 803.** 2 921 выглядят исправно; 831 имеют неполные tab facts; 334 содержат background load error code 6; 239 — scripts отсутствуют при нулевом числе background views. Частые хвосты `load → view → proxy_on` (1 450) и `load → view → proxy_on → dealloc → view → proxy_on` (934). `dealloc` фиксирует освобождение background view, не падение процесса; только 85 первичных iOS строк имеют явный `died_*` в доступном хвосте.
|
||
3. **Повторная проверка после extension reload, iOS 670 / macOS 232.** Это именно первый CPM init timeout после reload, вместе с `messaging.extension.reload.failed.daily`. Типичный хвост iOS `load → base_url_changed → view → proxy_on` (483); macOS `load → base_url_changed → view → proxy_on → dealloc → view → proxy_on` (87). `base_url_changed` здесь совпадает с reload и сам по себе не доказывает ошибку URL. Native snapshot уже выглядит исправно у 470 iOS / 125 macOS; причина отсутствия ответа там не локализована.
|
||
4. **macOS `other`, 1 050.** 713 выглядят исправно, 132 не имеют живого background view, 110 имеют только историческую context error, 47 — неверный controller без scripts. За все причины init неверный controller без scripts есть у **77/1 377 после tag и 0/28 до tag**; в stuck — **35/487 после tag и 0/2 до tag**. Init-сигнатура встречается с 10:34 до 23:21 UTC, а не только сразу после публикации в 09:46. Из 77 после tag 45 имеют `app_version_change=updated`. App update сам по себе не сохраняет старый `WKWebView`; эти строки не доказывают сценарий «вкладка осталась у старого контроллера после обновления». Требуется событие смены manager/controller при живой вкладке.
|
||
5. **iOS `tab.crash`, 199.** 47 имеют code 6, 37 — явную смерть background процесса, 35 — scripts отсутствуют и background view не создан. Название reason означает навигацию после crash вкладки, а не установленную смерть CPM worker.
|
||
|
||
Вторичный stuck сдвигает iOS-картину: background state 768/6 624 (11,6%) на первом сбое против 457/1 440 (31,7%) на stuck; no-scripts/zero-view 917/6 624 против 52/1 440. Это согласуется с временными startup misses и более устойчивой background-проблемой, **но это не переходная вероятность**: имена `.daily` считаются независимо. У iOS code 6 находится в 364 stuck снимках (в 363 `dealloc` раньше первой ошибки); WebKit регистрирует code 6 как `BackgroundContentFailedToLoad` после неудачной загрузки service worker. Причина отказа регистрации из пикселя неизвестна. Хвост `bg_events` ограничен 12 событиями и может обрезать старые события.
|
||
|
||
### Проверка по коду: что означает «исправный native-снимок»
|
||
|
||
1. **Пиксель измеряет отсутствие отдельного native-вызова.** После `navigationDidFinish` при наличии context (или после lifecycle `loaded`) monitor ждёт до 4 секунд `dashboardResponse`, который поступает только из `AutoconsentWebExtensionMessageHandler.handleRefreshCpmDashboardState` при вызове `refreshCpmDashboardState`. Если на `navigationDidFinish` extension отсутствует, действует отдельное 30-секундное ожидание с отменой измерения. По истечении 4-секундного таймера `reportFailure` собирает снимок **на тот момент**. `isAutoconsentExtensionLoaded` проверяет наличие embedded context в controller, не готовность CPM JS.
|
||
2. **`context_loaded=true` не означает завершённый startup.** В просмотренном `WebKit_sparse` `isLoaded()` означает наличие `m_extensionController`; он назначается до асинхронного `moveLocalStorageIfNeeded`, создания background view, регистрации scripts и `addInjectedContent()`. `bg_view_alive=true` означает живой `WKWebView`, а `bg_process_responsive=true` — что WebKit не признал его зависшим; это не проверка выполненного service worker listener. `tab_has_ext_scripts=true` проверяет список зарегистрированных `WKUserScript`, но не их выполнение в текущем документе. `handler_registered=true` проверяет запись в native router, но не факт обработки сообщения.
|
||
3. **`initResp` и наблюдаемый monitor сигнал расходятся в release bundle.** Content script отправляет `init` в background. В успешной ветке background делает `updateCpmDashboardState(...).then(refreshDashboardState(...))` **без await**, затем отдельно вызывает `chrome.tabs.sendMessage(..., {type: "initResp"})`. В ветках `config_unavailable`, `settings_missing`, `setting_disabled`, `site_disabled` он отправляет dashboard refresh и возвращается **без `initResp`**. Значит, `init failure.daily` возможен даже после отправки `initResp`, если dashboard refresh задержался или не сопоставился с документом; обратное тоже возможно: успешный health-сигнал без `initResp`.
|
||
4. **Есть конкретные места задержки и ложной атрибуции.** Background перед успешной веткой ждёт remote config и native setting/site checks; native request timeout в bundled JS — 20 секунд против 4-секундного окна monitor. Обновление dashboard state проходит через последовательную async-очередь и session storage. Monitor сопоставляет URL refresh с текущим документом по scheme/host/port/path/**query**; несоответствие или опоздание оставит timeout даже при работающем JS. Сколько случаев пришло из каждого участка, текущие пиксели не показывают.
|
||
|
||
Эта проверка проведена именно на bundle из release tags `1.210.0+macos` и `7.240.0+ios` (zip-файлы совпадают); текущий `main` уже содержит другой bundle. Приоритет следующей telemetry — отдельные времена `content init sent`, `background init received`, `initResp sent/received`, `dashboard refresh sent/received` и результат сопоставления URL.
|
||
|
||
## 3. Session restoration: ушли ли временные сбои?
|
||
|
||
**Нет.** После release tag есть 952 iOS и 68 macOS первых `session.restoration` failures. Соответствующий поздний `stuck.session.restoration.daily` встречается 118 / 8 раз, но без связи событий нельзя сказать, какие именно первые сбои восстановились. `recovered.without.extension.reload.daily` (1 555 iOS / 346 macOS) не содержит `reason`, поэтому доля временных session-restoration эпизодов также неизвестна.
|
||
|
||
Сравнение по имени пикселя на одинаковых пятидневных окнах после предыдущих release tags: iOS 7.238 (23–27 сентября) — 3 372 386 session restoration из 5 821 858 init daily (57,9%); iOS 7.239 (30 сентября – 4 октября) — 3 014 131 из 5 390 550 (55,9%). У iOS 7.240 за **первый неполный день** — 952 из 6 624 (14,4%). Это сильное изменение состава сигналов, совместимое с readiness gate, вошедшим в 7.240, но не оценка снижения риска на пользователя: новая версия имеет иной ранний cohort и нет DAU. macOS 1.208 (23–27 сентября) — 4 531/76 336 (5,94%); 1.209 (30 сентября – 4 октября) — 7 425/114 075 (6,51%); 1.210 после публикации — 68/1 377 (4,94%). На macOS явного снижения по этому proxy пока нет. Gate входит в macOS 1.210 / iOS 7.240 и по умолчанию включён, но фактическое состояние remote flag у отправителей не передано.
|
||
|
||
## 4. Исправления tab lifecycle: виден ли эффект?
|
||
|
||
[M1 tab identity fix](https://github.com/duckduckgo/apple-browsers/pull/6827) вошёл в macOS 1.209, а в 1.208 его нет; PR менял только macOS. Старые версии не посылали новые поля controller/scripts, поэтому сравним лишь общий proxy `stuck.other.daily / initialization.failed.after.other.daily`, **не частоту tab bugs**. В сопоставимых пятидневных окнах он равен 27 493/69 565 = **39,52%** на macOS 1.208 и 40 030/103 135 = **38,81%** на 1.209. Изменение на 0,71 процентного пункта мало и может отражать состав cohort/нагрузки; это не измеренный эффект фикса. Новый 1.210 после публикации даёт 418/1 050 = **39,81%**. Следовательно, по агрегатному тренду исчезновение tab breakage не подтверждено и не опровергнуто; именно относящиеся к нему события старых версий нельзя выделить без attribution.
|
||
|
||
## 5. After-stuck recovery mitigation: работает ли?
|
||
|
||
**Узкий функциональный сигнал есть.** [M4](https://github.com/duckduckgo/apple-browsers/commit/41656f8d17) входит в macOS 1.210 / iOS 7.240 и при подтверждённом hang пытается reload embedded extension (remote flag по умолчанию enabled). `recovered.after.extension.reload.daily` означает, что после reload текущего поколения снова поступил и был сопоставлен **native dashboard refresh**; получение `initResp` content script этот пиксель не проверяет. После публикации таких событий от `messaging_stuck` — **916 iOS / 354 macOS**. Все `recovered.after` вместе дают 963/1 440 = **66,9%** от числа stuck daily на iOS и 362/487 = **74,3%** на macOS, тогда как в предшествующих пятидневных окнах число пикселей этого имени относительно stuck daily составляло **3,23%** (iOS 7.239) и **2,70%** (macOS 1.209). Это большой скачок, согласующийся с автоматическим reload. Эти отношения **не success rate**: имена пикселей независимо throttled, эпизоды не соединены, одни установки могли дать несколько разных сигналов.
|
||
|
||
**Ограничение эффекта:** после публикации первый post-reload CPM probe всё ещё завершился timeout 670 раз на iOS и 232 на macOS, поэтому reload не гарантирует немедленного восстановления даже измеряемого native-вызова. Это не сбой самого API reload. Отдельный `debug.web.extension.reload.failed.daily` после публикации содержит один macOS error для `embedded / cpm_messaging_hung / load` и 223 iOS errors для **другого** `adBlocking / scriptlet_update / unload` extension; до публикации этих operation errors нет. На текущих данных M4 восстанавливает измеряемый background → native путь у части установок; нельзя вычислить долю stuck-эпизодов с полноценным `initResp` и CPM-функцией, которые остались невосстановленными, или изменение общей частоты init failures.
|
||
|
||
## 6. Другие CPM / Web Extension пиксели: что реально видно в системе
|
||
|
||
Сверка сделана по тем же версиям, полному UTC-дню 5 октября и с разделением по времени release tag. Полный наблюдаемый перечень `autoconsent` и `web.extension` с `pixel_id`, именем, platform suffix, частотой и числом записей — `related_pixels.tsv` (329 сгруппированных строк, включая отдельный pre-tag срез). `autoconsent_sources.tsv` делит каждый daily-пиксель по `fromExtension=1`, `0` и отсутствующему параметру; `autoconsent_summary.tsv` делает то же для standard summary и раскрывает счётчики событий внутри него. В таблице ниже только **после release tag**; записи до tag остаются в TSV отдельно.
|
||
|
||
| Сигнал после tag | iOS 7.240 | macOS 1.210 | Что он проверяет |
|
||
|---|---:|---:|---|
|
||
| `autoconsent_init.daily`, только `fromExtension=1` | **56 906** | **2 825** | Background Web Extension получил `init`, дошёл до разрешённой ветки CPM и отправил native pixel. Это происходит до отдельной попытки `chrome.tabs.sendMessage(initResp)` и не подтверждает доставку ответа. |
|
||
| `autoconsent_summary`, только `fromExtension=1` | **324 549** standard-отчётов; **1 760 177** накопленных `init` | **54 421** отчёт; **213 878** `init` | Фоновый скрипт накопил события и позже отправил сводку через native. Это наблюдение многих успешных background→native проходов, но не denominator для `init failure.daily`: другое окно, другой тип частоты, эпизоды не соединены. |
|
||
| `web_extension_loaded.daily` | 61 902 | 2 870 | Manager загрузил хотя бы одно extension; имя не указывает, что это CPM, и ничего не говорит о конкретной вкладке. |
|
||
| `web_extension_state_checked.daily` | 62 004 | 2 805 | Lifecycle coordinator проверил набор ожидаемых/загруженных extension в manager. |
|
||
| `web_extension_embedded_not_loaded.daily` | **3 389** | **1** | Во время хотя бы одной проверки embedded extension ожидался, но отсутствовал в manager. Не проверяет controller/scripts конкретной вкладки. |
|
||
| `web_extension_load_error.daily` | 4 | 4 | Ошибка общей загрузки extension. Это отдельный сигнал от CPM timeout; по одному имени тип extension не известен. |
|
||
|
||
**Почему `autoconsent_init` нужно делить по источнику.** Одинаковое имя пикселя используют Web Extension и старый Autoconsent user script. Всего `init.daily` после tag пришло 60 720 на iOS и 3 351 на macOS; из них `fromExtension=1` — 56 906/2 825, `fromExtension=0` — 1 100/1, без параметра — 2 714/525. В release-коде user script ставит `0` только при доступном Web Extensions API, но недоступном autoconsent extension; отсутствие параметра также не доказывает работу extension. `.daily` ограничен по общему имени, поэтому нельзя считать этот split долей всех init-вызовов. Standard summary не имеет такого daily throttle: extension-источник дал 324 549/54 421 сводок с `init` счётчиками, но сводка запускается двухминутным alarm и может прийти существенно позже навигации.
|
||
|
||
**Функциональные CPM-события тоже есть:** с `fromExtension=1` после tag получены `autoconsent_popup-found.daily` 31 363 iOS / 1 918 macOS и `autoconsent_done.daily` 23 050 / 1 578; `disabled-for-site.daily` 4 763 / 694. Они показывают, что Web Extension действительно обнаруживает и обрабатывает popup на части навигаций. Ошибки `error_optout.daily` 513/107 и `error_reload-loop.daily` 280/49 относятся к обработке popup, а не к доставке `initResp`. `self-test-fail.daily` 153/97 **не имеет `fromExtension=1`** в этих данных и потому не служит сигналом ошибки extension. Все эти имена общие с legacy user script, поэтому исходный `fromExtension` обязателен.
|
||
|
||
**Lifecycle и соседние семейства:** `web_extension_embedded_installed.daily` 51 958/1 756 и `embedded_upgraded.daily` 804/741 отражают операции установки, а не исполнение content script. Есть отдельные пиксели install/uninstall, ad-blocking, dark-reader, scriptlet fetch/validation/install и `web_extension_ad_blocking_not_loaded`; полный набор реально поступивших имён и чисел в `related_pixels.tsv`. Они полезны для общей работоспособности Web Extensions, но не локализуют CPM `init`-timeout. Все debug CPM health-пиксели и actual `web_extension_reload_failed` разобраны выше в разделах 1–5.
|
||
|
||
**Вывод для крупнейшей группы init failure:** в совокупности данные подтверждают, что Web Extension широко запускается и обрабатывает CPM на этих билдах. Они **не подтверждают**, что в 3 717 iOS / 911 macOS строках с исправным native-снимком background получил именно тот `init`, отправил `initResp` и content script его принял. Нет event/pixel для `content init sent`, `background init received` с общим navigation ID, `initResp sent/received`, начала/результата `refreshCpmDashboardState` или исхода сопоставления URL. Сопоставить `autoconsent_init`/`summary` с конкретным `initialization.failed` по текущей анонимной телеметрии нельзя. На macOS лишь 1 общий `embedded_not_loaded.daily` при 77 init failures с controller mismatch без scripts: это согласуется с тем, что глобальный manager может быть исправен, пока отдельная вкладка прикреплена неверно.
|
||
|
||
Код release tags: `WebExtensionPixelFiring.swift` (семейства и частоты CPM); `WebExtensionManager.swift` (`.loaded` после общего load); `WebExtensionLifecycleCoordinator.swift` (`stateChecked`/`embeddedNotLoaded`); платформенные `AutoconsentMessageHandlerDelegate` (пересылка Web Extension пикселей); `AutoconsentUserScript.swift` (то же имя с `fromExtension=0` или без параметра); release bundle background `handleAutoConsentMessage`, `firePixel`, `sendSummaryPixel` (`init`, `initResp` и двухминутная сводка).
|
||
|
||
## 7. Общие `bg_events` у init failure и последующего stuck
|
||
|
||
**Кодовая связь сильнее совпадения строк.** Внутри `CPMMessagingHealthMonitor.reportFailure` первый timeout создаёт `Episode`, а `messaging.stuck` отправляется только после ещё одного timeout проверки, начатой позже первого failure (для confirmation — не session-restoration). Episode живёт не более пяти минут. Имя stuck наследует `failureReason` **первого** timeout; диагностика `stuck` снимается с вкладки **второго** timeout. `CPMMessagingDiagnosticsRecorder` сохраняет общую историю background context между этими снимками и при каждом берёт последние 12 событий; первый failure не очищает её. Поэтому `stuck.bg_events` действительно может содержать события, случившиеся до первого init failure. Новый context или вытеснение старых событий обрывает это наблюдение.
|
||
|
||
**Проверка на выгрузке после release tag.** Для каждого stuck искался более ранний init failure той же платформы и с тем же `reason` в окне пяти минут. Требовалось точное совпадение порядка токенов `bg_events` без возрастных суффиксов и совместимость всех возрастных buckets после прибавления разницы timestamps (допуск ±30 с). Для сильного сигнала оставлены цепочки длиной **не менее шести** событий. Контроль: timestamps stuck сдвинуты на +10 и −10 минут внутри post-tag интервала; все прочие правила те же. Это тест **обогащения совпадениями**, не восстановление анонимного reporter ID. Время пикселя может отличаться от времени снимка из-за доставки.
|
||
|
||
| После tag | Stuck daily | С цепочкой ≥6 | Совпало с более ранним init | Контроль +10 / −10 мин | Ровно один кандидат в выгрузке |
|
||
|---|---:|---:|---:|---:|---:|
|
||
| iOS 7.240 | 1 440 | 1 025 | **693** | 255 / 270 | 467 |
|
||
| macOS 1.210 | 487 | 368 | **168** | 29 / 19 | 148 |
|
||
|
||
Иными словами, длинные цепочки у последующих stuck совпадают с цепочками более ранних init failures значительно чаще, чем при соседнем сдвинутом времени. Но «ровно один кандидат» означает один подходящий **анонимный пиксель** в этой выгрузке, а не доказанный один пользователь. `.daily` мог подавить настоящий первый пиксель, а часто встречающиеся цепочки способны совпасть у разных пользователей.
|
||
|
||
**Какие группы дают эту связь:**
|
||
|
||
| Группа stuck после tag | Stuck всего | Длинная цепочка совпала | Контроль +10 мин | Уникальный кандидат с той же группой init |
|
||
|---|---:|---:|---:|---:|
|
||
| iOS, исправный native-снимок | 789 | **437** | 199 | 249 |
|
||
| iOS, background load error 6 | 338 | **152** | 20 | 142 |
|
||
| macOS, исправный native-снимок | 335 | **138** | 21 | 120 |
|
||
| macOS, только история context error | 79 | **20** | 6 | 14 |
|
||
|
||
Пример совместимой пары iOS с одним кандидатом: в init-хвосте после повторных `dealloc→view→proxy_on` стоит `error_wkwebextensioncontexterrordomain:6@30s`; 41 секунду спустя stuck содержит **тот же порядок 12 токенов**, а последний error уже помечен `@2m`. Это согласуется с одним сохраняющимся background-сбоем через два timeout, но без reporter/episode ID остаётся вероятностным сопоставлением. В группе с исправным native-снимком найдены аналогичные пары с тем же длинным `load→view→proxy_on→dealloc→view→proxy_on...` и более старшими age buckets на stuck: два timeout могут происходить без нового видимого WebKit lifecycle-события.
|
||
|
||
Метод намеренно слаб для коротких хвостов `load` и `load→view→proxy_on`, в том числе startup `no scripts/no view` и macOS controller mismatch: у 35 macOS stuck с неверным controller только 3 хвоста длиной ≥6, поэтому отсутствие длинного match здесь ничего не опровергает. Из этих таблиц **нельзя вычислять вероятность перехода отдельного init failure в stuck**: нет идентификатора установки или episode, а `.daily` дедуплицирует имена. Тем не менее сочетание state machine в коде и превышения над контролем подтверждает, что stuck-пиксели отражают повторные timeout-эпизоды, а не просто новое имя для одиночного init failure.
|
||
|
||
Воспроизведение: `match_event_chains.py` → `chain_match_summary.tsv`, `chain_match_transitions.tsv` в каталоге выгрузки. Исходные строки: `initialization_daily.tsv` и `events_daily.tsv`. Числа выше используют только after-tag строки; до публикации остаются отдельно.
|
||
|
||
## Источники и воспроизведение сверки
|
||
|
||
Локальная выгрузка и код подсчёта: `cpm-public-attribution-2026-10-06/` в `/Users/admin/DuckDuckGo/apple-browsers.git`. `inventory.tsv` — полный CPM daily/count перечень и actual reload errors; `release_cutover_daily.tsv` — все CPM daily и actual reload daily по обе стороны release-tag timestamp; `initialization_daily.tsv` и `events_daily.tsv` — PII-free строки attribution с точным timestamp; `release_cutover_groups.tsv` и `release_cutover_reasons.tsv` — группы и причины по обе стороны tag, воспроизводятся `analyze_cutover.py`; `daily_groups_exclusive.tsv`, `daily_families.tsv`, `daily_reasons.tsv`, `daily_signatures.tsv` — сводки **полного дня, включая pre-tag**; `release_cpm_daily_trends.tsv` — 982 строки дневных трендов шести версий. Код поведения: `CPMMessagingHealthMonitor.swift` (`reportFailure`, `reportSuccess`), `WebExtensionManager.swift` (`onConfirmedHang`, `reloadEmbeddedExtensionAfterCPMMessagingHang`), `WebExtensionLifecycleCoordinator.swift` (navigation gate), `PixelKit.swift` (лимит `.daily` по имени и UTC). Снимок законченного дня; для вывода о динамике incidence нужен следующий полный UTC-день новой версии и знаменатель активных установок той же версии.
|