From 185065e599bd0a84ff0e4e35d6c2cf9b38f51782 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Mon, 5 Oct 2026 15:05:05 +0600 Subject: [PATCH] [2026-10-05] eagle: work/projects/cpm-web-extension-breakage-validation.md --- .../cpm-web-extension-breakage-validation.md | 86 ++----------------- 1 file changed, 9 insertions(+), 77 deletions(-) diff --git a/work/projects/cpm-web-extension-breakage-validation.md b/work/projects/cpm-web-extension-breakage-validation.md index 7c7beab2..8e8bd4cc 100755 --- a/work/projects/cpm-web-extension-breakage-validation.md +++ b/work/projects/cpm-web-extension-breakage-validation.md @@ -26,89 +26,21 @@ ## Как получить и проверить attribution data -**Источник:** Grafana ClickHouse datasource `dekdbd6bqq1hcd` (`grafana-clickhouse-datasource`), таблица `metrics.pixels`. Использовался API через `/Users/admin/DuckDuckGo/grafana-dashboards/tools/grafana-query/read-dashboard.py`, **не** браузер и не локальный Apple worktree. Авторизация этого helper — `grafana-auth`/SSO либо `GS_GRAFANA_API_KEY`; токены в документ не записывать. [Board](https://grafana.duckduckgo.com/d/dfon7whlilnggb/cpm-weird-error-investigation?orgId=1) — визуальная сверка, SQL ниже — источник приведённых чисел. +**Тулсет:** `/Users/admin/Developer/grafana-data-tools/` — `query.py`, `queries/coverage.sql`, `queries/tab-signatures.sql`, `queries/tab-chains.sql`, `README.md`. Это постоянные файлы; `/tmp/cpm-*` из прошлой сессии не нужны. Runner импортирует точный helper `/Users/admin/DuckDuckGo/grafana-dashboards/tools/grafana-query/read-dashboard.py`; тот вызывает Grafana `/api/ds/query` и авторизуется через `tools/grafana-auth/grafana-auth` (SSO) либо `GS_GRAFANA_API_KEY`. Datasource: ClickHouse UID `dekdbd6bqq1hcd`, type `grafana-clickhouse-datasource`, таблица `metrics.pixels`. Токены не записывать в заметки или логи. -1. Сохранить нужный SQL-блок ниже в `/tmp/cpm-query.sql`. Для исторической проверки оставить UTC-окно `[2026-09-28, 2026-10-05)`; для public заменить начало на фактический день публикации и указать **только завершённые UTC-дни**. Сверять `date`, `agent`, `appVersion`, суффикс `.count`/`.daily` и наличие `context_loaded` с Kibana. -2. Из `/Users/admin/DuckDuckGo/grafana-dashboards` выполнить этот read-only API-запрос. Заменить `FROM`/`TO` вместе с датами SQL; пустой Grafana frame без ошибки перепроверить повторным запросом, а не трактовать как ноль: +**Проверенные команды** (2026-10-05; `--start` включительно, `--end` исключительно, UTC): ```bash -uv run --with requests python3 - /tmp/cpm-query.sql 2026-09-28T00:00:00Z 2026-10-05T00:00:00Z <<'PY' -import importlib.util -import sys -from pathlib import Path - -path = Path('tools/grafana-query/read-dashboard.py').resolve() -spec = importlib.util.spec_from_file_location('grafana_reader', path) -reader = importlib.util.module_from_spec(spec) -spec.loader.exec_module(reader) -target = {'refId': 'A', 'rawSql': Path(sys.argv[1]).read_text(), - 'editorType': 'sql', 'queryType': 'table', 'format': 1} -raw = reader.query_ds_query('dekdbd6bqq1hcd', 'grafana-clickhouse-datasource', - [target], reader.parse_time(sys.argv[2]), reader.parse_time(sys.argv[3])) -result = raw.get('results', {}).get('A', {}) -if result.get('error'): - raise SystemExit(result['error']) -if not result.get('frames'): - raise SystemExit('No data frame returned; retry/inspect Grafana response') -for frame in result['frames']: - print('\t'.join(f['name'] for f in frame['schema']['fields'])) - for row in zip(*frame['data']['values']): - print('\t'.join(map(str, row))) -PY +cd ~/Developer/grafana-data-tools +uv run --with requests query.py coverage --start 2026-09-28 --end 2026-10-05 +uv run --with requests query.py tab-signatures --start 2026-09-28 --end 2026-10-05 --agent ddg_mac_desktop --version 1.210 +uv run --with requests query.py tab-chains --start 2026-09-28 --end 2026-10-05 --agent ddg_mac_desktop --version 1.210 +uv run --with requests query.py tab-signatures --start 2026-09-28 --end 2026-10-05 --agent ddg_ios --version 7.240 ``` -**SQL 1 — покрытие по всем версиям, включая пиксели без attribution.** Не фильтровать версии заранее: так обнаруживаются 1.211/7.241 и большой legacy iOS-поток без новых полей. `attributed` — строки с live-параметром `context_loaded`; `rows` — все stuck-пиксели. `.count` и `.daily` не складывать. +`coverage` проверяет **все** версии и оба суффикса `.count`/`.daily` до фильтрации по attribution (`attributed` = есть live-поле `context_loaded`; `chain_present` = есть `bg_events`). Ожидаемая историческая проверка: macOS 1.210 за 28 сентября — 4 октября даёт 131 `.count` и 82 `.daily` attributed; iOS 7.240 — 6 и 5. `tab-signatures` даёт `131 / 88 / 24 / 17 / 10 / 7 / 6` для macOS и `6 / 5 / 0 / 0 / 0 / 0 / 0` для iOS (`reports / repeated_dealloc / scripts_absent / controller_mismatch / mismatch_after_update / same_controller_zero_views / zero_views_after_update`). `tab-chains` показывает ordered `bg_events` **до** stuck с группировкой по reason, controller, view и update. Все четыре команды реально выполнены через API; SQL лежит в `queries/`, а не только в этой заметке. -```sql -SELECT - toString(date) AS day, - agent, - arrayStringConcat(arraySlice(splitByChar('.', extractURLParameter(request, 'appVersion')), 1, 3), '.') AS app_version, - multiIf(match(pixel, '\\.daily(\\.ios\\.(phone|tablet))?$'), 'daily', - match(pixel, '\\.count(\\.ios\\.(phone|tablet))?$'), 'count', 'other') AS frequency, - count() AS rows, - countIf(extractURLParameter(request, 'context_loaded') != '') AS attributed, - countIf(extractURLParameter(request, 'bg_events') != '') AS chain_present -FROM metrics.pixels -WHERE date >= toDate('2026-09-28') AND date < toDate('2026-10-05') - AND agent IN ('ddg_ios', 'ddg_mac_desktop') - AND pixel_id = 'debug' - AND pixel LIKE 'debug.web.extension.cpm.messaging.stuck.%' -GROUP BY day, agent, app_version, frequency -ORDER BY day, agent, app_version, frequency; -``` - -**SQL 2 — macOS 1.210 tab-сигнатуры и предшествующие события.** Только attributed `.count` stuck; каждое число — репорты, не люди. `bg_events` упорядочены oldest→newest, но содержат лишь ограниченный хвост и грубые age buckets; `dealloc` не равен доказанному process death. Проверить эту же выборку с `.daily` для чувствительности к повторным репортам. - -```sql -WITH events AS ( - SELECT - extractURLParameter(request, 'tab_in_context') AS tab_in_context, - extractURLParameter(request, 'tab_controller_match') AS controller_match, - extractURLParameter(request, 'tab_has_ext_scripts') AS scripts_present, - extractURLParameter(request, 'bg_view_creations') AS view_creations, - extractURLParameter(request, 'app_version_change') AS version_change, - splitByChar(',', decodeURLComponent(extractURLParameter(request, 'bg_events'))) AS chain - FROM metrics.pixels - WHERE date >= toDate('2026-09-28') AND date < toDate('2026-10-05') - AND agent = 'ddg_mac_desktop' AND pixel_id = 'debug' - AND pixel LIKE 'debug.web.extension.cpm.messaging.stuck.%' - AND match(pixel, '\\.count$') - AND extractURLParameter(request, 'appVersion') LIKE '1.210.%' - AND extractURLParameter(request, 'context_loaded') != '' -) -SELECT - count() AS reports, - countIf(arrayCount(x -> startsWith(x, 'dealloc@'), chain) >= 2) AS repeated_dealloc, - countIf(scripts_present = 'false') AS scripts_absent, - countIf(scripts_present = 'false' AND controller_match = 'false' AND tab_in_context = 'true') AS controller_mismatch, - countIf(scripts_present = 'false' AND controller_match = 'false' AND version_change = 'updated') AS mismatch_after_update, - countIf(scripts_present = 'false' AND controller_match = 'true' AND view_creations = '0') AS same_controller_zero_views, - countIf(scripts_present = 'false' AND controller_match = 'true' AND view_creations = '0' AND version_change = 'updated') AS zero_views_after_update -FROM events; -``` - -**Методика интерпретации:** сначала SQL 1 проверяет покрытие версии/платформы и отсутствие attribution; затем SQL 2 группирует совместные признаки вместо чтения одиночного поля как root cause. Для конкретной сигнатуры дополнительно сгруппировать `bg_events` с сохранением порядка, reason из имени pixel, `app_version_change` и `bg_view_alive`; сверить с исходным кодом `CPMMessagingDiagnosticsRecorder` и созданием `WebExtensionManager` на `apple-browsers.git/main`. Internal forced-failure testing, app update и повторные `.count` могут смещать выборку. Public user-rate и recovered/permanent требуют отдельного user-level числителя и DAU **той же версии**; эти SQL их не вычисляют. +**Методика:** сначала `coverage` сверяет `date`, `agent`, `appVersion`, `.count`/`.daily` и наличие attribution с Kibana; огромный legacy iOS-поток без `context_loaded` не включать в анализ новых параметров. Затем `tab-signatures` проверяет совместные признаки, `tab-chains` — порядок событий. В коде `apple-browsers.git/main` сверять их с `CPMMessagingDiagnosticsRecorder` и созданием `WebExtensionManager`, а не со старым M3 worktree. При пустом ответе Grafana без data frame повторить запрос: это не подтверждённый ноль. `.count` — репорты, `.daily` — дневные пиксели, не уникальные пользователи; `bg_events` — ограниченный хвост с грубыми age buckets, `dealloc` не доказывает process death. Internal forced-failure testing может смещать все сигнатуры. Для public заменить даты на фактический rollout и брать только завершённые UTC-дни, отдельно iOS/macOS и app version; affected-user rate и recovered/permanent требуют отдельного user-level числителя и DAU **той же версии** — эти команды их пока не считают. ## Исторический статус и PR-план (2026-09-23—09-25)