25 KiB
tags, updated, status
| tags | updated | status | |||
|---|---|---|---|---|---|
|
2026-11-03 | draft |
CPM stuck-messaging project: where we are, Nov/3
Internal metrics, keep out of public PRs. Task: CPM: root-cause the stuck embedded extension messaging. Details: cpm-post-release-analysis-2026-10-27, cpm-status-2026-10-15.
What the project has achieved
Goal: permanent failures below 1% of DAU (0.2-0.5% = success). Permanent = no answer for 5 minutes, repeated or needed a reload.
Now (macOS 1.214, iOS 7.244, Nov/2)
- iOS: 0.44% of DAU (0.39% on 7.243, same day). Inside the target.
- macOS: 1.89%. Above the target.
Fixes shipped
- P3, OS<26.6 limit – improves extension startup after a restart from up to 32 s → 0.1 s on iOS/macOS 26.6+ (lab tests). iOS 26.6+: "background missing" failures 8.2% → 0.3% of DAU (users with any failure 14.6% → 8.3%). Effect on permanent not measurable yet (one day, 90k users).
- macOS: pinned tab lost on window close, tab merging – real bugs fixed, UI tests added. They caused under 1% of permanent failures, about 3x fewer after the fix (macOS 1.211 → 1.213).
- P1 – recovery after a slow wake of the extension. Triggers for 19% of iOS and 3% of macOS daily users (Nov/2).
- P2 – retry after load error 6. Triggers for 1.7% of iOS and 0.2% of macOS daily users (Nov/2).
- Network process crash – restart after the networking process crashes. Triggers for 6.6% of iOS and 4.2% of macOS daily users (Nov/2).
- State restoration race – restored tabs wait up to 5 s for the extension to start. Init failures on restore: iOS ~20% → ~8.5% of DAU, macOS no clear change.
Hypotheses rejected
- Deallocation race (A/B, flag) – fix shipped behind a flag never turned on, no relation in the data.
- Reload on the stuck signal – 46% of macOS cases still got no answer.
- P4 – reload the page when the extension starts late. Off by default, never triggered in production.
Fixes going public next week (Nov/9)
- Upgrade before loading – load the new extension version first on launch. Removes part of the permanent failures right after an update (macOS 0.26% of DAU, iOS 0.05%).
- Error 6 restart – restart the extension when a load error leaves it broken. Up to 0.08% of DAU permanent on iOS, negligible on macOS.
- Together: macOS 1.89% → about 1.6%, iOS 0.44% → about 0.3-0.35%. Estimates, upper bounds.
Data now (% of DAU)
Permanent failures, daily trend (macOS 1.213 / iOS 7.243: Oct/26 → Nov/2; 1.214 / 7.244: Nov/2):
- macOS: 2.07% → 3.07% (Oct/27 peak) → 1.90%; 1.214: 1.89%.
- iOS: 0.30% → 0.42% (Oct/27 peak) → 0.39%; 7.244: 0.44%.
By group, % of DAU. Trend is failures: previous version (1.213 / 7.243) on Oct/26 → previous version on Nov/2 → new version (1.214 / 7.244) on Nov/2. Shares and permanent are for the new version.
| Group | Platform | Trend | % of all failures | Permanent | % of all permanent |
|---|---|---|---|---|---|
| Init on wake | macOS | 8.80% → 8.50% → 5.89% | 39% | 0.68% | 36% |
| Init on wake | iOS | 1.56% → 2.34% → 1.74% | 22% | 0.06% | 15% |
| Tab not connected | macOS | 1.18% → 0.90% → 1.26% | 8% | 0.42% | 22% |
| Init after extension update | macOS | 5.40% → 0.92% → 2.94% | 20% | 0.26% | 14% |
| Init after extension update | iOS | 0.37% → 0.13% → 0.34% | 4% | 0.05% | 12% |
| Init after load | macOS | 2.73% → 3.41% → 1.73% | 11% | 0.19% | 10% |
| Init after load | iOS | 1.86% → 2.21% → 2.54% | 32% | 0.18% | 42% |
| Error 6 | macOS | 0.14% → 0.17% → 0.11% | 1% | 0.01% | 1% |
| Error 6 | iOS | 0.50% → 1.32% → 0.67% | 8% | 0.08% | 18% |
| Background missing | macOS | 1.06% → 1.13% → 0.20% | 1% | 0.03% | 2% |
| Background missing | iOS | 0.63% → 6.90% → 0.27% | 3% | 0.01% | 3% |
| Other | macOS | 4.75% → 4.17% → 2.92% | 19% | 0.30% | 16% |
| Other | iOS | 1.95% → 1.10% → 2.45% | 31% | 0.04% | 10% |
1.214 / 7.244 is one day of early adopters (macOS about 9k DAU, iOS 110k), a rough picture.
Plan
This week, cleanup:
- Feature flags cleanup:
- Remove fix and flag (did not work or never ran): cpmBackgroundGraveyardTreatment, cpmLateBackgroundDocumentRecovery, cpmMessagingHangRecovery, cpmBackgroundLoadRecovery.
- Remove with the diagnostics: cpmDiagnosticsRecorder, cpmBackgroundDelegateProxy.
- Keep the fix, remove the failsafe flag: cpmNetworkProcessRecovery, cpmStableBaseURL, webExtensionSharedPinnedTabWindowClose, webExtensionStateRestorationGate, cpmBackgroundLoadRestart (after Nov/9 data).
- Pixel data cleanup: remove the temporary pixels and episode parameters that only served the investigation.
Next, metrics:
- Permanent breakage metrics: a small fixed set, rates against DAU, added to the health indicators dashboard. Task to create.
- Extension lifecycle metrics: find where the startup message stops, to diagnose the wake failures. Needs privacy review and releases, data in 1-2 release cycles. Task to create.
- Data check of the Nov/9 fixes: Analyze attribution data after stage 3.
Open:
- No fix for macOS wake failures and tab not connected. If the new metrics show nothing actionable, close the project and reset the target.
Состояние на 10 ноября: стопка PR очистки
Раскладка. PR 1 #8025, ветка alex/cpm-episode-metrics (ворктри cpm-episode-metrics), конец 6928847174 (merge origin/main ba38c366b5), запушен, метка skip pixel check, ждёт ревью. PR 2 (pixel cleanup по плану), ветка alex/cpm-pixel-cleanup (ворктри cpm-pixel-cleanup) от 6928847174, переделка в работе, сессию просили остановить, что успела изменить, неизвестно, не запушена. Старые ветки с неверно скроенным PR 2 (чистка вместе с новым пикселем, вердиктом по концу страницы и полем background) и метками диагностики init wake: alex/cpm-breakage-pixel (e6419bb910) и alex/cpm-init-diagnostics (adb28e643d), не запушены, использовать как заготовки. Расширение: правки в ~/DuckDuckGo/duckduckgo-privacy-extension, ветка alex/cpm-init-progress (PR #3727), без коммита (метка page_init_sent в shared/js/cpm.js; config_read, settings_checked, site_checked и обработка метки в cookie-prompt-management.js); тесты расширения не запускались.
PR 1 сделано. Удалены флаги и код: graveyard, hang recovery, late document recovery, P2 cpmBackgroundLoadRecovery (с веткой повтора загрузки), флаг webExtensionSharedPinnedTabWindowClose (фикс остался, безусловный). Удалены пиксели background_load_retry, late_document_reload, startup_response_missing, install_readiness, upgrade_readiness, background_wake, перечисления CPMRecoveryFix/CPMRecoveryOutcome, класс CPMBackgroundReadinessRecovery, значение cpm_messaging_hung в reload_trigger. Определения пикселей удалены сознательно. Планировщик восстановления после error 6 (cooldown, пауза в фоне, pending, возобновление) оставлен и проверяет теперь флаг cpmBackgroundLoadRestart. Остаются флаги-выключатели: cpmBackgroundLoadRestart, cpmNetworkProcessRecovery, cpmStableBaseURL, cpmUpgradeBeforeLoad, stateRestorationGate.
PR 2, целевая реализация (по плану Pixel data cleanup, без нового пикселя и без новой логики детекта). Удалить: CPMMessagingDiagnosticsRecorder, CPMMessagingDiagnostics, CPMBackgroundWebViewDelegateProxy, CPMBackgroundReadinessObserver, CPMDiagnosticsFeatureFlags и их тесты; флаги cpmDiagnosticsRecorder и cpmBackgroundDelegateProxy (PrivacyFeature, FeatureFlag обеих платформ); проводку в WebExtensionManager, +iOS, +macOS, +EmbeddedExtensions, +NativeMessaging; параметр appSession: в iOS makeManager и его вызов в MainCoordinator; cpmAppSessionDiagnostics в macOS AppDelegate; пункт «CPM Background WebContent PID» в macOS WebExtensionsDebugMenu; diagnosticsProvider в CPMMessagingHealthMonitor. Урезать пиксель web_extension_cpm_episode (имя остаётся): убрать 8 диагностических булевых, bg_events, четыре поля загрузки фона, was_stuck, interrupted, response_after; оставить reason, outcome, tabs_affected, repeat_failures, reload_state; обновить CPMMessagingEpisodeReport, определения в web_extensions.json5 и записи в params_dictionary.json5 обеих платформ (macOS сохраняет суффикс ["daily_count"] и параметры appVersion, pixelSource, channel). Таймеры, лайфсайкл эпизода, детект и restart-наблюдение не менять. Не добавлять новый пиксель, вердикт по концу страницы, поле background, метки cpmInitProgress. Изменение параметров существующего пикселя потребует метку Danger.
Дальше по плану. Перманентный пиксель рядом со старым эпизодом на один релиз, затем старый убрать: вердикт по концу страницы, поле «пробуждение или тёплый фон» через didCreateBackgroundWebView, мгновенная поломка по cpmStage; учесть 9 находок ревью старого PR 2 (определение macOS не совпадало с отправляемым, молчащие замеры старше 5 минут отбрасывались, нет минимального времени жизни, падение процесса вкладки засчитывалось как поломка, background по загрузке дольше 1 с неверно, утечка navigationWaitContexts, нет теста на разбор cpmStage). Диагностика init wake: приём cpmInitProgress, last_stage, расширение (#3727 плюс метки). Вне PR: записи об удалённых подфичах в privacy-configuration. Не измерено: эффект P3, stateRestorationGate, pinned-tab по версиям; доля потерянных отчётов; время жизни страниц с молчащим расширением.
Решения по данным (ClickHouse, 8–9 ноября, счёт count). P2 убран: пикселя результата нет (115 тысяч повторов в день на iOS), при оставшемся view повтор не завершается (фон это service worker). Restart 9 ноября: iOS 7.245 118 перезапусков, 81% ответов страницы; macOS 1.215 54 перезапуска, 65%, выборка маленькая, контроля нет. Network recovery оставлен: iOS 388 тысяч срабатываний в день, 59% числа эпизодов, эффект не измерен (network_restarted убрали из отчёта в том же релизе), измерять выключением удалённого флага. Таймер 4 с: на iOS 7.244 6,0% эпизодов закрылись поздним ответом на ту же страницу, 42% из них позже 30 с, 19,4% без ответа за 5 минут. Доступа к статусу расширения из приложения нет (в SDK нет доступа к миру расширения, evaluateJavaScript на фоне не достаёт до воркера); порт или heartbeat как проверка здоровья не годятся (открытый порт откладывает выгрузку фона).
Данные из сессии. ~/Developer/grafana-data-tools/ и запуск SQL через Grafana с SSO через системный Chrome (grafana-auth --channel chrome). Счёт пикселей: metrics.pixels_aggregation (pixel_id = 'web'); параметры: metrics._pixels_new только с pixel_id = 'web' и одним днём.
10 ноября, вечер: разрез PR и tech design
Разрез. Ветка alex/cpm-breakage-pixel (e6419bb910) смешивала чистку с новым пикселем и удаляла эпизодный пиксель сразу, против плана. Новая раскладка: PR 1 #8025 без изменений. PR 2 alex/cpm-pixel-cleanup (ae725e6b12, поверх 6928847174) уже содержит чистую чистку по плану: удалены recorder, CPMMessagingDiagnostics, delegate proxy, readiness observer и два флага с проводкой и пунктом debug-меню macOS; эпизодный пиксель урезан до reason, outcome, tabs_affected, repeat_failures, reload_state, без нового пикселя и без изменения таймеров. Проверено: swift test пакета WebExtensions 1104 теста, 0 падений; SwiftLint strict без замечаний на CPMMessagingHealthMonitor, WebExtensionPixelFiring, WebExtensionManager. Не проверено: сборка приложений iOS и macOS, валидатор определений пикселей (CI). Не запушена, PR не создан (нужна ссылка на задачу Asana и ревьюер; метка Danger, потому что параметры существующего пикселя меняются). PR 3 (перманентный пиксель) и PR 4 (метки init и last_stage, плюс расширение) ещё не начаты, ждут ревью tech design. Старые alex/cpm-breakage-pixel и alex/cpm-init-diagnostics остаются только источником.
Что неверно в e6419bb910 (проверено по коду): тихие замеры старше 5 минут сбрасываются (discardExpiredState); нет минимального времени жизни страницы; падение процесса вкладки засчитывается поломкой; background берётся из «загрузка ≥ 1 с» и одного общего сигнала; страницы, живущие до выхода приложения, не оцениваются; определение macOS без pixelSource и channel, суффиксы от iOS; утечка navigationWaitContexts; нет теста на разбор cpmStage.
Tech design (черновики, ревьюер не назначен):
- cpm-permanent-breakage-metric-tech-design-2026-11-10: два пикселя (
web_extension_cpm_breakageсreasonиbackground,web_extension_cpm_breakage_persistentбез параметров), оценка страницы по концу документа, минимум 30 с жизни, серия из 3 подряд. Старые пиксели (эпизодный,background_load_restart,network_process_recovery_applied) удаляются в том же PR 3 вторым коммитом, без задержки на релиз; остаются только эти два. - cpm-wake-init-detection-tech-design-2026-11-10: метки
cpmInitProgressиз расширения,last_stageв пикселе,response_sentкак здоровый сигнал, изменение расширения (PR #3727, веткаalex/cpm-init-progress; меткиpage_init_sent,config_read,settings_checked,site_checkedв рабочем дереве без коммита и без запуска тестов).
Открытые вопросы для ревью: порог 30 с и серия 3; сохранять серию между запусками; считать ли response_sent здоровым до проверки без слушателя; шесть меток или меньше; нужен ли флаг для нового пикселя.
11 ноября: переделка стопки на детект по молчанию вкладки
Решение. Детект «страница оценивается по концу документа» (серия из 3, буфер ответов, background) заменён на «вкладка молчит»: после коммита подходящей http(s) страницы с загруженным расширением вкладка, от которой расширение не прислало сообщение 30 с времени приложения, считается молчащей и репортится один раз. Поздний ответ всё равно считается ответом, ответ за 2 с до коммита засчитывается, время приостановки приложения не считается, таймеров нет, проверка идёт на событиях. Сообщение привязывается к вкладке по идентификатору вкладки расширения, который приложение узнаёт один раз по единственной совпавшей странице (host + path); сообщение, которое не привязать, отвечает за все подходящие вкладки (лучше пропустить поломку, чем записать рабочую вкладку). Публичного и приватного API для id вкладки расширения нет (проверено по заголовкам SDK и исходникам WebKit).
Пиксели. Остаются два: web_extension_cpm_breakage (параметр tabs: one или many, то есть была ли уже молчащая вкладка с последнего сообщения расширения) и web_extension_cpm_breakage_persistent (вторая молчащая вкладка в серии, один раз на серию, без параметров). Старые web_extension_cpm_episode, web_extension_cpm_background_load_restart, web_extension_cpm_network_process_recovery_applied удалены в PR 2, отдельного PR 5 нет. Метки cpmInitProgress и last_stage (PR 4) привязаны к молчащей вкладке; метка никогда не считается ответом.
Раскладка (локально, по одному коммиту на ворктри, не запушено, PR не создавались).
- PR 2
alex/cpm-pixel-cleanup:de24abf498на6928847174. Вся чистка, монитор урезан до сопоставления вкладок и сброса бюджета перезапусков; вкладки шлют одно событие коммита с флагомisEligible(iOS: error page не измеряется, back/forward читается на старте навигации), события старта/финиша/ошибки навигации удалены. 1004 теста пакета. - PR 3
alex/cpm-breakage-metric:9b9281e849на PR 2. Молчание по вкладкам, два пикселя, определения обеих платформ, адаптеры. После ревью: вкладки, замолчавшие в одной проверке, всеmany; сообщение до коммита отвечает и для ещё не выученной вкладки; монотонные часы; refresh без ответа (config_unavailable/settings_missing) не ответ, но сбрасывает бюджет перезапусков. 1048 тестов пакета. - PR 4
alex/cpm-init-marks:d99f23020dна PR 3. Метки init по вкладкам иlast_stage; метка учитывается только для текущей страницы вкладки, метки до коммита приписываются новой странице. 1071 тест пакета. Расширение#3727не менялось. Старые коммиты сохранены какrefs/backup/cpm-old-pr3(82d29f412a) иrefs/backup/cpm-old-pr4(e8dc96dec2) в репозиторииapple-browsers.git.
Проверено: swift test пакета на каждой ветке, мутационные проверки (ответ после проверки, нет сдвига на приостановку, persistent на одной вкладке, нет допуска, метка считается ответом, ранг понижается), валидаторы определений пикселей обеих платформ, SwiftLint strict на изменённых файлах, xcodebuild build-for-testing macOS. Не запускалось: app-hosted тесты macOS и iOS (кроме трёх тестов NavigationPixelNavigationResponderTests, запущенных до запрета), сборка iOS (падает на двух script phases, окружение), реальный WebKit.
Открытые проблемы подхода: привязка первого сообщения по URL (две вкладки с одним URL, смена path в SPA), страницы без инъекции расширения выглядят молчащими, оценка только на событиях (молчащая вкладка в простаивающем приложении, которое потом убили, не репортится), допуск 2 с может засчитать сообщение предыдущей страницы, потеря нативного канала даёт tabs=many, бюджет перезапусков теперь сбрасывается любым сообщением текущего расширения, нет базовой серии после удаления старых пикселей, expires с новых определений убран: метрика постоянная (REVIEW-pixels).