338 lines
22 KiB
Markdown
338 lines
22 KiB
Markdown
---
|
||
type: tech-risks
|
||
work_item_id: ET-009
|
||
title: "Технические риски — ET-009: Активация EnduroRussia + Wikiloc"
|
||
version: 1
|
||
status: approved
|
||
created_at: 2026-06-01
|
||
authors:
|
||
- "agent:architect"
|
||
---
|
||
|
||
# Технические риски — ET-009
|
||
|
||
Технические риски этапа активации двух новых GPS-источников. Бизнес-риски —
|
||
в BRD §6 ET-009. Многие риски наследуются от ET-008 (R-1, R-5, R-9 из
|
||
`docs/work-items/ET-008/10-tech-risks.md`); здесь — специфика ET-009.
|
||
Шкала: вероятность (Н/С/В) × влияние (Н/С/В).
|
||
|
||
## R-1 — Wikiloc меняет HTML → парсер возвращает 0 треков
|
||
|
||
- **Описание:** Парсер Wikiloc опирается на regex-извлечение
|
||
`<a href="/trails/…/<id>">` и `<h1>` для названия. Wikiloc может в
|
||
любой момент изменить разметку (новый шаблон, JS-rendering) → парсер
|
||
вернёт 0 треков.
|
||
- **Вероятность / Влияние:** В / С.
|
||
- **Митигация:**
|
||
- Парсер уже спроектирован **graceful**: `return` без `raise` при
|
||
отсутствии match'ей regex (см. `wikiloc.py::_extract_track_paths`).
|
||
- Health-эндпоинт показывает `tracks_by_source.wikiloc = 0` после
|
||
прогона → видимый сигнал оператору.
|
||
- Unit-тест UT-WL-01 на снимке `wikiloc-search-page1.html` — при
|
||
смене разметки CI зелёным быть не сможет, разработчик обновит
|
||
фикстуру + парсер за 1 итерацию.
|
||
- `gps_sources.yaml::wikiloc.enabled: false` — мгновенное отключение
|
||
без deploy при критической поломке.
|
||
- **Наследник от:** ET-008 R-1 (general).
|
||
|
||
## R-2 — Wikiloc банит IP mva154
|
||
|
||
- **Описание:** Скрейпер с фиксированного IP может попасть в чёрный
|
||
список Wikiloc'а (особенно при ошибках rate-limit или
|
||
накоплении 1000+ запросов в сутки). Pipeline начнёт получать 403/429
|
||
на все запросы → новых треков не будет.
|
||
- **Вероятность / Влияние:** С / В.
|
||
- **Митигация:**
|
||
- `rate_limit_sec: 10` — самый консервативный rate в проекте.
|
||
- `max_tracks_per_run: 50` — soft-cap на первом прогоне; ≤ 150
|
||
запросов на одну активацию.
|
||
- `User-Agent` с контактным URL — платформа может связаться
|
||
через email до бана.
|
||
- **Graceful-stop** на 403/429 — не агрессивный retry, не вызывает
|
||
дополнительных запросов.
|
||
- **Мониторинг первых 3 прогонов** оператором; при систематических
|
||
403 → `enabled: false` + новый ADR-update «Wikiloc deprecated».
|
||
- Запрет использования прокси через сторонний IP (нарушает дух
|
||
прозрачности; см. ET-008 R-5).
|
||
|
||
## R-3 — EnduroRussia API меняет схему ответа
|
||
|
||
- **Описание:** `enduro_russia.py::_parse_gpx` ожидает поля
|
||
`id`, `name`, `difficulty`, `created_at` в JSON-items. Платформа
|
||
может добавить/переименовать поля.
|
||
- **Вероятность / Влияние:** Н / С.
|
||
- **Митигация:**
|
||
- Парсер использует `.get()` с дефолтами — отсутствие необязательных
|
||
полей не валит.
|
||
- Отсутствие `id` → запись пропускается (`continue`), не валит весь
|
||
прогон.
|
||
- Контрактный smoke-тест `tests/contract/test_endurorussia_api_smoke.py`
|
||
с маркером `@pytest.mark.network` — запускается nightly или вручную,
|
||
сигнализирует о поломке внешнего API до пропущенного cron-прогона.
|
||
- Pipeline-error не лоадит всю БД: `errors_json` фиксирует, оператор
|
||
видит через `/health`.
|
||
|
||
## R-4 — Расхождение конфига `enduro-russia.ru` (с дефисом) vs
|
||
реального `endurorussia.ru` (без дефиса)
|
||
|
||
- **Описание:** До ET-009 `gps_sources.yaml::enduro_russia.base_url`
|
||
содержит `https://enduro-russia.ru` (с дефисом), но реальный
|
||
домен — `https://endurorussia.ru` (без дефиса; парсер по default
|
||
использует именно его). При активации `enabled: true` без фикса URL
|
||
парсер использовал бы default из кода, но `external_url` сохранённых
|
||
треков опирался бы на `base_url` из конфига → fragmentation
|
||
external_url'ов между «корректным» и «дефис-вариантом».
|
||
- **Вероятность / Влияние:** Случилось (известный bug в конфиге) /
|
||
В (при активации).
|
||
- **Митигация:**
|
||
- **F-01 в BRD/TRZ** — фикс URL в одно изменение.
|
||
- **Регрессионный тест UT-ER-05** — проверяет, что парсер
|
||
сохраняет URL без дефиса при передаче `base_url` без дефиса.
|
||
- One-shot UPDATE для существующих треков (опционально, см.
|
||
`07-infra-requirements.md` §4.1).
|
||
|
||
## R-5 — EnduroRussia и Wikiloc — двойник одного и того же трека → массовые дубли
|
||
|
||
- **Описание:** Авторы часто публикуют одну и ту же поездку и на
|
||
Wikiloc, и на EnduroRussia (Wikiloc даже сохраняет `creator=Wikiloc`
|
||
в GPX мета-теге, что подтверждается на практике). Без правильно
|
||
работающего dedup'а в БД появятся два трека с одинаковой геометрией.
|
||
- **Вероятность / Влияние:** В / С.
|
||
- **Митигация:**
|
||
- `compute_dedup_key` (ADR-006) основан на `bbox+length+date`, который
|
||
при достаточно похожих координатах и одной дате попадает в один
|
||
bucket → upsert ON CONFLICT мержит.
|
||
- **Интеграционный тест IT-DEDUP-01** — задаёт фикстуру `wikiloc-track.gpx`
|
||
с координатами, совпадающими с одним из EnduroRussia-треков; проверяет
|
||
итоговое объединение `sources_json=['enduro_russia','wikiloc']`.
|
||
- Метаданные при merge — берутся от source с большим `source_priority`
|
||
(`enduro_russia=80 > wikiloc=70`); `external_urls` — оба сохраняются.
|
||
- Если на практике dedup пропускает (например, точное время / точный
|
||
bbox slightly off): план отступления ADR-006 §8 (сузить
|
||
length-bucket, добавить activity).
|
||
|
||
## R-6 — Cron первого прогона превышает окно из-за rate-limit Wikiloc
|
||
|
||
- **Описание:** При больших cap'ах `max_tracks_per_run` и rate-limit
|
||
10 сек × 3 запроса/трек первый прогон Wikiloc может занять часы.
|
||
- **Вероятность / Влияние:** С / Н.
|
||
- **Митигация:**
|
||
- `max_tracks_per_run: 50` — soft-cap → ≤ 25 минут на прогон Wikiloc.
|
||
- EnduroRussia при rate-limit 5 сек × 305 треков ≈ 25 минут — окей.
|
||
- Cron автоматизация **отложена** до отдельного DevOps-task'а
|
||
после двух успешных ручных прогонов; оператор контролирует
|
||
длительность.
|
||
- Опционально: `timeout 21600 docker compose ...` в cron (ET-008
|
||
R-11 уже фиксирует).
|
||
|
||
## R-7 — UI-фильтр «Источник» не подхватывает новые ID
|
||
|
||
- **Описание:** Если в ET-008 UI-фильтр (`#gps-source-grid`) построен
|
||
с захардкоженным списком `[osm]`, новые источники не появятся как
|
||
чекбоксы.
|
||
- **Вероятность / Влияние:** Н / С.
|
||
- **Митигация:**
|
||
- **Дизайн ET-008**: UI строит фильтр **динамически** из ответа
|
||
`/api/gps-tracks/health.tracks_by_source` (источники с count > 0).
|
||
После первого прогона ET-009 — фильтр сам покажет 3 чекбокса.
|
||
- UI-тест TC-UI-04 (в `04b-ui-test-cases.md` ET-008) расширен для
|
||
ET-009: проверяет наличие 3 чекбоксов после двух прогонов.
|
||
- Маппинг `SOURCE_LABELS` (в `gps_tracks.js`) расширяется явно
|
||
в ET-009 — даёт корректные читаемые названия.
|
||
|
||
## R-8 — Цветовая палитра в `style.json` / `style-dark.json` не содержит новых ID → линии серые
|
||
|
||
- **Описание:** В ET-008 match-expression `line-color` может содержать
|
||
только `osm`; новые источники получат fallback-серый.
|
||
- **Вероятность / Влияние:** В / Н.
|
||
- **Митигация:**
|
||
- **REQ-F-13** явно требует обновить match-expression с тремя
|
||
источниками + fallback.
|
||
- Code-review-чеклист: проверить наличие `enduro_russia`, `wikiloc`
|
||
в `paint.line-color` обоих стилей.
|
||
- При пропуске: визуальный регресс легко заметен в smoke-тесте
|
||
(TC-UI-05).
|
||
|
||
## R-9 — Дамп БД (резервная копия с старым URL) — orphan записи
|
||
|
||
- **Описание:** Если на test-сервере есть резервная копия БД, в которой
|
||
`external_urls_json` содержит `enduro-russia.ru` (с дефисом),
|
||
то после фикса URL новые treki будут иметь `endurorussia.ru` (без
|
||
дефиса), а старые — `enduro-russia.ru`. Это не криминал, но
|
||
фрагментация атрибуции.
|
||
- **Вероятность / Влияние:** Н / Н.
|
||
- **Митигация:**
|
||
- На практике `enduro_russia` до ET-009 был `enabled: false` →
|
||
таких записей **нет**. Риск гипотетический.
|
||
- Опциональный one-shot `UPDATE tracks SET external_urls_json = REPLACE(...)`
|
||
— фиксируется в `14-deploy-log.md` если применяется.
|
||
|
||
## R-10 — ADR-010 / ADR-012 регрессировали в `proposed`
|
||
|
||
- **Описание:** Между моментом написания BRD/TRZ ET-009 и моментом
|
||
активации (merge → deploy) кто-то откатил статус ADR в `proposed`.
|
||
Pipeline-guard заблокирует source с `skipped_license`.
|
||
- **Вероятность / Влияние:** Н / В.
|
||
- **Митигация:**
|
||
- **F-03 / REQ-F-05** — pre-check перед активацией:
|
||
```bash
|
||
grep -E "^status:" docs/work-items/ET-008/06-adr/ADR-010-enduro-russia-licensing.md
|
||
grep -E "^status:" docs/work-items/ET-008/06-adr/ADR-012-wikiloc-licensing.md
|
||
```
|
||
Оба должны вернуть `accepted`. Иначе — STOP и эскалация архитектору.
|
||
- Интеграционный тест IT-LIC-01 проверяет работу pipeline-guard'а:
|
||
подменяет `accepted → proposed` в копии ADR-010 и убеждается, что
|
||
pipeline скипает source с `status='skipped_license'`.
|
||
- **Наследник от:** ET-008 R-9.
|
||
|
||
## R-11 — Пользовательский opt-in для новых источников
|
||
|
||
- **Описание:** Пользователи с уже сохранённым `localStorage['gps-tracks-sources']
|
||
= ["osm"]` после ET-009 **не увидят** треки EnduroRussia/Wikiloc на
|
||
своих устройствах — клиент сохраняет старое значение, новые источники
|
||
по умолчанию не enabled в UI.
|
||
- **Вероятность / Влияние:** В / Н.
|
||
- **Митигация:**
|
||
- Это **сознательное решение** UX (см. `08-data-requirements.md` §4.2):
|
||
добавление источников без согласия пользователя — нарушение
|
||
принципа без сюрпризов.
|
||
- Чекбоксы новых источников появятся в `#sheet-gps-filters`
|
||
automatically (через health-endpoint), пользователь может включить
|
||
их вручную.
|
||
- В release-notes (если они есть в проекте) — фиксируем «появились
|
||
два новых источника, активация в фильтре».
|
||
|
||
## R-12 — Wikiloc-парсер сохраняет описание / автора несмотря на ADR-012
|
||
|
||
- **Описание:** ADR-012 §3 явно запрещает сохранять `description` и
|
||
`user` для Wikiloc. Если реализация парсера не уважает этот запрет
|
||
(например, `TrackInsert.description` заполняется), нарушение
|
||
licensing-условий.
|
||
- **Вероятность / Влияние:** Н / В.
|
||
- **Митигация:**
|
||
- **Текущая реализация:** `wikiloc.py::_parse_gpx` возвращает
|
||
`TrackInsert(description=None, user=None)` (зашито в коде).
|
||
- Unit-тест UT-WL-05 проверяет, что `description=None` и `user=None`
|
||
в возвращаемом `TrackInsert`.
|
||
- Code-review checklist в `12-review.md`: при любом изменении
|
||
парсера Wikiloc убедиться, что эти поля остаются null.
|
||
|
||
## R-13 — Тестовые фикстуры устаревают
|
||
|
||
- **Описание:** Снимки HTML/JSON, использованные в unit-тестах,
|
||
отражают состояние API/HTML **на момент снятия**. Через 6-12
|
||
месяцев платформа может изменить разметку, и фикстуры станут
|
||
неактуальны. Тесты пройдут (фикстура соответствует тесту), но
|
||
парсер **не будет работать** в production.
|
||
- **Вероятность / Влияние:** С / С.
|
||
- **Митигация:**
|
||
- Контрактный smoke-тест `test_endurorussia_api_smoke.py`
|
||
(`@pytest.mark.network`, nightly) — проверяет реальную схему
|
||
API, ловит расхождение.
|
||
- Аналогичный smoke для Wikiloc **не** делаем (риск бана IP при
|
||
регулярных запросах; ETC-009 §«REQ-F-16»).
|
||
- Health-эндпоинт показывает `tracks_by_source.wikiloc` после
|
||
каждого продакшн-прогона; устойчивое 0 — сигнал.
|
||
- При устаревании фикстуры — снимаем заново (1 час работы), парсер
|
||
обновляем (1-3 часа).
|
||
|
||
## R-14 — Производительность endpoint деградирует при росте кол-ва треков
|
||
|
||
- **Описание:** REQ-NF-02 ET-008 фиксирует p95 ≤ 300 мс на bbox с
|
||
≤ 500 треков. После ET-009 в БД появятся ещё ≤ 250 треков —
|
||
пренебрежимо относительно 5000 OSM.
|
||
- **Вероятность / Влияние:** Н / Н.
|
||
- **Митигация:**
|
||
- R-tree индекс по `geom` (ADR-005) → O(log n) bbox-prefetch.
|
||
- AC-20 — нагрузочный тест 100 запросов после первого прогона.
|
||
- При деградации — анализ EXPLAIN QUERY PLAN; добавление индекса
|
||
`idx_tracks_source` опционально (out of scope ET-009).
|
||
|
||
## R-15 — Конфликт MAPPING-таблиц для одной активности
|
||
|
||
- **Описание:** EnduroRussia маппит `motorcycle → moto`, Wikiloc
|
||
тоже `motorcycle → moto` — корректно. Но: EnduroRussia при
|
||
отсутствии match'а в MAPPING возвращает `enduro` (fallback),
|
||
Wikiloc — `moto`. Для одного и того же трека (попавшего в оба
|
||
источника) при merge получим `activity_type` от source с большим
|
||
`source_priority` = `enduro_russia` → `enduro`. Это **OK**: priority
|
||
делает выбор детерминированным.
|
||
- **Вероятность / Влияние:** Н / Н.
|
||
- **Митигация:**
|
||
- Принято as is. Уточнение в `08-data-requirements.md` §3.4.
|
||
- Unit-тесты UT-ER-04 и UT-WL-06 проверяют отдельные MAPPING'и.
|
||
|
||
## R-16 — Регрессия e2e-тестов ET-008
|
||
|
||
- **Описание:** Расширение `style.json` / `gps_tracks.js`
|
||
атрибуцией и цветами может случайно сломать существующие
|
||
selectors / визуальные тесты ET-008.
|
||
- **Вероятность / Влияние:** Н / С.
|
||
- **Митигация:**
|
||
- **AC-19** — все e2e ET-008 (E-01..E-41) должны пройти после
|
||
мерджа ET-009.
|
||
- Регрессионный прогон `pytest tests/e2e/ -v` — обязательный
|
||
шаг CI.
|
||
|
||
## R-17 — Pipeline скипает source из-за неправильного `license_adr` path
|
||
|
||
- **Описание:** Pipeline-guard `_check_license_adr` читает YAML
|
||
front-matter файла по пути из `license_adr`. Если путь опечатан
|
||
(например, `ADR-12-...` вместо `ADR-012-...`), guard вернёт false →
|
||
`status='skipped_license'`.
|
||
- **Вероятность / Влияние:** Н / В.
|
||
- **Митигация:**
|
||
- Pre-deploy check: убедиться, что `license_adr` указывает на
|
||
реально существующий файл с `status: accepted`.
|
||
- При первом запуске pipeline в test-среде оператор смотрит
|
||
`pipeline_runs[-1].status`; если `skipped_license` —
|
||
диагностирует и исправляет до merge в main.
|
||
- Pydantic-валидация `gps_sources.yaml` в pipeline ET-008 уже
|
||
требует обязательное `license_adr` поле; отсутствие — exception
|
||
при старте.
|
||
- **Наследник от:** ET-008 R-9.
|
||
|
||
## Сводная таблица
|
||
|
||
| ID | Риск | Вер. | Влияние | Класс | Статус |
|
||
|---|---|---|---|---|---|
|
||
| R-1 | Wikiloc меняет HTML | В | С | Высокий | принят + graceful + быстрое отключение |
|
||
| R-2 | Wikiloc банит IP mva154 | С | В | Высокий | rate-limit 10s + cap 50 + UA + monitor |
|
||
| R-3 | EnduroRussia API меняет схему | Н | С | Низкий | smoke-тест + graceful + health |
|
||
| R-4 | Расхождение URL `enduro-russia` vs `endurorussia` | Случилось | В | Высокий | F-01 фикс + UT-ER-05 |
|
||
| R-5 | Дубли EnduroRussia/Wikiloc | В | С | Средний | dedup-key + IT-DEDUP-01 |
|
||
| R-6 | Cron первого прогона долго | С | Н | Низкий | `max_tracks_per_run=50` + ручной прогон |
|
||
| R-7 | UI-фильтр не подхватит | Н | С | Низкий | динамика из health + SOURCE_LABELS |
|
||
| R-8 | Стили без новых цветов | В | Н | Низкий | REQ-F-13 + review + smoke |
|
||
| R-9 | Orphan записи с старым URL | Н | Н | Низкий | гипотетический (БД чистая); опц UPDATE |
|
||
| R-10 | ADR-010/012 регрессировали в proposed | Н | В | Высокий | pre-check + IT-LIC-01 |
|
||
| R-11 | Пользовательский opt-in для новых источников | В | Н | Низкий | сознательный UX-compromise |
|
||
| R-12 | Wikiloc сохраняет description/user | Н | В | Высокий | parser-design + UT-WL-05 + review |
|
||
| R-13 | Фикстуры устаревают | С | С | Средний | smoke-test + health + ручной refresh |
|
||
| R-14 | Деградация endpoint | Н | Н | Низкий | R-tree + AC-20 |
|
||
| R-15 | Конфликт MAPPING | Н | Н | Низкий | source_priority детерминирует |
|
||
| R-16 | Регрессия ET-008 e2e | Н | С | Низкий | AC-19 + pytest e2e |
|
||
| R-17 | Неправильный `license_adr` path | Н | В | Высокий | pre-deploy check + Pydantic |
|
||
|
||
**Высокие классы:**
|
||
- R-1, R-2 — операционные, ожидаемые для скрейп-источника Wikiloc;
|
||
митигация — multi-layer (graceful + monitor + конфиг-kill-switch).
|
||
- R-4 — known bug в конфиге, прямо адресован REQ-F-01.
|
||
- R-10, R-17 — критичны для legal compliance; митигация многослойная
|
||
(pre-check + integration-тест + Pydantic).
|
||
- R-12 — критичен для соблюдения ADR-012; митигация через design +
|
||
UT-WL-05 + review.
|
||
|
||
**Блокирующих рисков нет.** Высокие классы требуют внимания на этапе
|
||
разработки и code review.
|
||
|
||
## Эскалация
|
||
|
||
- **arch:major-change** — **не выставляется** (см. ADR-013
|
||
§«Классификация»). Изменение не вводит новых архитектурных компонентов.
|
||
- **back-to:analysis** — не требуется. ТЗ полное, BRD ясный,
|
||
open-questions в `TRZ §6` закрыты дефолтными решениями.
|
||
- Эскалация архитектору требуется **только** при срабатывании R-10
|
||
(ADR в `proposed` на момент активации). Тогда задача останавливается
|
||
до повторного апрува ADR.
|