[2026-10-05] eagle: work/projects/cpm-web-extension-breakage-validation.md
This commit is contained in:
1 parent
d136dba735
commit
185065e599
1 file changed
+9
-77
@@ -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)
|
||||
|
||||
|
||||
Reference in new issue
Block a user