21 KiB
21 KiB
type, work_item_id, title, version, status, created_at, authors
| type | work_item_id | title | version | status | created_at | authors | |
|---|---|---|---|---|---|---|---|
| tech-risks | ET-008 | Технические риски — ET-008: GPS-треки с публичных платформ | 1 | approved | 2026-06-01 |
|
Технические риски — ET-008
Технические риски этапа разработки и эксплуатации. Бизнес-риски — в BRD §6 (пересечение есть, здесь акцент на технические митигации). Шкала: вероятность (Н/С/В) × влияние (Н/С/В).
R-1 — Парсер источника ломается при изменении HTML
- Описание: ADR-010/011 источники (
enduro_russia,ttrails) скрейпят HTML-страницы. Платформа может в любой момент изменить разметку (новый шаблон, JS-rendering) → парсер перестаёт извлекать треки. - Вероятность / Влияние: В / С.
- Митигация:
- Каждый source в отдельном модуле (
src/api/gps_tracks/sources/<name>.py); падение одного не валит других (ADR-007 §I-A). - Pipeline пишет
status=errorвpipeline_runs; оператор видит через/api/gps-tracks/health. - Параметризированные тесты с фикстурами HTML-снапшота — при первом упавшем прогоне разработчик обновляет фикстуру и парсер за 1 итерацию.
- При двух неудачных прогонах подряд — алерт (
07-infra-requirements.md§10.1). На MVP — ручная проверка. - Конфиг
gps_sources.yaml::enabled: false— мгновенное отключение источника без deploy.
- Каждый source в отдельном модуле (
R-2 — Ложные коллизии дедупа
- Описание: ADR-006 алгоритм
bbox+length+date bucketдетерминированно мерджит треки с похожими параметрами. На треках безcreated_at(от источников без даты) — гарантированный merge всех таких треков в одном bbox/length. На дата-датасете — возможны коллизии для популярных маршрутов (двое разных гонщиков проехали тот же 30-км круг в один день). - Вероятность / Влияние: С / С.
- Митигация:
- BRD §5 фиксирует допустимую метрику «< 5% дублей»; QA-скрипт
scripts/dedup_audit.pyпроверяет на выборке 100 треков (04-test-plan.yaml). - При провале метрики — план отступления ADR-006 §8 (сузить length-bucket, добавить activity в ключ).
- Если меняется формула dedup_key — полный rebuild БД (
rm + python -m scripts.gps_collect); регенерация ≤ 6 часов. - Документация в
08-data-requirements.md§3.2 для оператора.
- BRD §5 фиксирует допустимую метрику «< 5% дублей»; QA-скрипт
R-3 — Pipeline повреждает БД
- Описание: Бaг в Python-коде upsert (ADR-006 §6) при ON CONFLICT может оставить БД в несогласованном состоянии (битый JSON в
sources_json, частично записанная transaction). SQLite + WAL обычно atomic per-statement, но composite upsert может рассогласоваться. - Вероятность / Влияние: Н / В.
- Митигация:
- Все upsert операции — внутри SQLite
BEGIN IMMEDIATE / COMMIT(atomic transaction). - Ежедневный backup
data/gps_tracks.sqlite(07-infra-requirements.md§4.4). - При повреждении:
cp backups/gps_tracks-<date>.sqlite data/gps_tracks.sqlite+ cache-clear API. RTO ≈ 1–2 минуты. - Полный rebuild:
rm gps_tracks.sqlite && docker compose --profile batch run --rm gps-collector— ≤ 6 часов. - Изоляция в отдельной БД (ADR-005 D-A) гарантирует, что повреждение не затронет
centralfederal.sqlite(OSM-данные).
- Все upsert операции — внутри SQLite
R-4 — Размер БД превышает 2 ГБ
- Описание: REQ-NF-03 предел
data/gps_tracks.sqlite— 2 ГБ. На MVP-объёме (5000 треков ≈ 105 МБ) запас 20×. Но при расширении на РФ или при отсутствии работающего GC размер может вырасти линейно. - Вероятность / Влияние: Н / С.
- Митигация:
- Health-эндпоинт отдаёт
db_size_mb— оператор видит. - Месячный GC
--gcудаляет треки старше 5 лет (07-infra-requirements.md§7.1). - При устойчивом росте > 2 ГБ — миграция на PostGIS (отдельный work item; контракт API стабилен, см. ADR-005 §«Технический долг»).
- Алерт
db_size_mb > 2000— пока ручная проверка (post-MVP — автоматический).
- Health-эндпоинт отдаёт
R-5 — IP mva154 банится источником
- Описание: Скрейпер с фиксированного IP может попасть в чёрный список платформы (особенно при ошибках rate-limit). Pipeline начинает возвращать 429/403 на все запросы → source не пополняется.
- Вероятность / Влияние: С / С.
- Митигация:
- Rate-limit в
gps_sources.yamlper-source (1 сек для OSM, 5 сек для скрейп-источников). - Корректный User-Agent с контактом — платформа может связаться, прежде чем банить.
- Backoff на 429 (
TRZ §6.3) — exponential до 3 попыток. pipeline_runs.errors_jsonфиксирует HTTP-коды → оператор видит.- При бане — приостановить source (
enabled: false), связаться с платформой, при необходимости отключить полностью. - Прокси через сторонний IP — не закладывается (нарушает дух прозрачности).
- Rate-limit в
R-6 — Pipeline жрёт ресурсы и деградирует API во время прогона
- Описание: На время прогона
gps-collectorконтейнер активен, скачивает GPX, парсит, пишет в БД. Если ресурсы не ограничены —httpx+shapelyмогут уйти в GC-storm; SQLite write lock конкурирует с API readers. - Вероятность / Влияние: Н / С.
- Митигация:
- Docker
cpus: "1.0",mem_limit: 512m(07-infra-requirements.md§9.2). Pipeline не вытесняет API даже на одно-CPU-сервере. - WAL-mode позволяет API читать БД во время записи pipeline'а (ADR-005 W-A).
- Cron в 03:00 UTC = 06:00 MSK — низкий traffic.
- Async-генератор
parser.collect()— pipeline pulls треки по одному, не накапливает в памяти больше одного (ADR-007 §4).
- Docker
R-7 — Дублирование tile-утилит между main.py и gps_tracks/mvt.py
- Описание: ADR-005 §8 принимает дублирование
tile_to_bbox/wkb_to_coords/simplify_coords(≈ 100 строк) ради избежания риска регрессии существующего слояtrails. Любая правка формулы упрощения требует синхронной правки в двух местах. - Вероятность / Влияние: С / Н.
- Митигация:
- Комментарий в обоих файлах
# ET-008/ADR-005-§8: дубль из main.py; при добавлении третьего MVT-источника — вынести в src/api/tiles_util.py. - Code review-чеклист: при правке
simplify_coordsв одном файле — проверить второй. - При появлении третьего MVT-источника — обязательный рефакторинг (отдельный work item).
- Комментарий в обоих файлах
R-8 — GeoJSON-эндпоинт превышает SLA на плотных bbox
- Описание: REQ-NF-02 предел 300 мс p95 на bbox с ≤ 500 треков. На реальной географии возможны bbox в плотных регионах (например, Подмосковье на z=12) где
total_in_bbox > 2000. SQL даже с R-tree может проигрывать при ORDER BY + post-filter source. - Вероятность / Влияние: С / Н.
- Митигация:
- Cutoff
limit=500обрезает результат на уровне SQL. - Cutoff zoom 12 — на z=11 уходим в MVT-кэш, нагрузки на GeoJSON-endpoint нет.
- R-tree обеспечивает O(log n) bbox-prefetch.
- Дополнительный индекс по
length_m DESCдля ORDER BY (длинные треки приоритетнее) — фиксируется в коде; SQLite сделает sort быстро на 500 строках. - Если SLA не выполняется — server-side кэширование GeoJSON-ответов по
(bbox_quantized, activity, source)(post-MVP).
- Cutoff
R-9 — Лицензионный ADR не enforced
- Описание: ADR-007 §6 требует, чтобы pipeline отказывался загружать source-parser без
accepted-ADR. Если разработчик обходит проверку (например, забывает добавитьlicense_adr:поле вgps_sources.yaml) — pipeline пойдёт скрейпить без юридического подтверждения. BRD §4 явно требует «зелёного света». - Вероятность / Влияние: Н / В.
- Митигация:
- Pydantic-валидация
gps_sources.yaml— полеlicense_adrобязательное, отсутствие → exception при старте pipeline. - Дополнительная проверка в runtime:
license_adrдолжен указывать на существующий файл; YAML frontmatterstatus: accepted. Иначе source skip сstatus: skipped_license. - Code review-чеклист в
12-review.md: при добавлении source вgps_sources.yamlобязательна ссылка на accepted-ADR. - QA-кейс:
tests/api/test_gps_tracks_licensing_guard.py— поднимает pipeline сproposed-ADR, проверяет что source пропускается.
- Pydantic-валидация
R-10 — Cache-clear endpoint доступен извне
- Описание:
POST /api/gps-tracks/cache/clearсбрасывает LRU. Если эндпоинт доступен через/enduro/— атакующий может вызывать его в цикле, обнуляя кэш и заставляя сервер постоянно перегенерировать тайлы (DoS). - Вероятность / Влияние: Н / С.
- Митигация:
07-infra-requirements.md§3.1: nginx-правилоlocation = /enduro/api/gps-tracks/cache/clear { allow 172.16/12; deny all; }.- Pipeline ↔ API дёргает endpoint напрямую через docker-сеть, минуя nginx → работает.
- При появлении CSP-заголовка —
connect-src 'self'блокирует внешние POST'ы из браузера (но это уже есть).
R-11 — Pipeline зависает (вечная проблема скрейперов)
- Описание: Парсер одного источника попадает в бесконечный pagination loop или висит на медленном HTTP. Cron-job не завершается, следующий cron-тик попадает на ту же задачу.
- Вероятность / Влияние: Н / С.
- Митигация:
httpx.AsyncClient(timeout=30)— таймаут на каждый запрос.- Per-source максимум треков на прогон (
max_tracks_per_runвgps_sources.yaml, default 5000) — стопорит pagination loop. - Cron-окно (3 дня между прогонами) > потенциального hang-окна; overlapping runs — два docker container'а, ресурсы изолированы; следующий cron не блокируется первым.
- Опционально:
timeout 21600 docker compose ...в cron — kill после 6 часов (REQ-NF-02). На MVP — не обязательно, но рекомендовано.
R-12 — Несогласованность UI/style при setStyle()
- Описание: При переключении тёмной темы / спутника
map.setStyle()сбрасывает все runtime-добавленные source/layer.rebuildMapOverlays()пересоздаёт; если порядок вызовов нарушен — слой публичных треков может оказаться поверх маршрута или ниже спутника. - Вероятность / Влияние: Н / С.
- Митигация:
restorePublicTracksState()вызывается вrebuildMapOverlays()послеrestoreTrailsState(), доrestorePoiState()и маршрута/GPX (TRZ REQ-F-19).- AC-12 «Переживание setStyle()» проверяет: чекбокс работает после смены темы.
- Идемпотентные
if (!map.getSource(id)) map.addSource(...)— паттерн из ADR-004 R-6.
R-13 — Конфликт с ET-006 (личные GPX)
- Описание: ET-006 хранит личные GPX треки в
window.gpxTracksи отображает какgpx-layer-*. Если ET-008 случайно использует тот же layer-id или event-handler — взаимная коллизия. - Вероятность / Влияние: Н / С.
- Митигация:
- Префикс
gps-tracks-*для всех новых id (source, layer, halo) — конфликт исключён. window.gpsTracksLayer≠window.gpxTracks(TRZ §4.4).- Z-order:
gps-tracks-layer-*<gpx-layer-*(личные приоритетнее, как уточняется в TRZ §7.1). - AC-10 «Совместимость с ET-006» проверяет совместное отображение.
- Префикс
R-14 — Конфликт с ET-007 (спутник + halo)
- Описание: ET-007 уже реализовал паттерн halo для trails на спутнике через
applyTrailHaloVisibility()(ADR-004 §9). ET-008 добавляет два новых halo (gps-tracks-halo-mvt-satellite,gps-tracks-halo-geo-satellite) и расширяетapplyBaseLayer(). - Вероятность / Влияние: Н / С.
- Митигация:
- Новые halo-слои добавляются в оба
style.json/style-dark.jsonсvisibility: none— по тому же паттерну ET-007. applyBaseLayer()(ET-007) расширяется одним блоком (см. TRZ §7.2):const gpsHaloOn = (currentBase === 'satellite' && layerState.publicTracks); setLayoutProperty('gps-tracks-halo-mvt-satellite', 'visibility', gpsHaloOn && activeMode === 'mvt' ? 'visible' : 'none'); setLayoutProperty('gps-tracks-halo-geo-satellite', 'visibility', gpsHaloOn && activeMode === 'geo' ? 'visible' : 'none');- AC-11 «Halo на спутнике» проверяет.
- Новые halo-слои добавляются в оба
R-15 — Pipeline не находит зависимости (defusedxml, pyyaml)
- Описание: При смене образа без полного rebuild —
gps-collectorстартует с старымrequirements.txt→ ImportError. - Вероятность / Влияние: Н / Н.
- Митигация:
- Deploy-runbook (§7) явно требует
docker compose buildперед запуском нового pipeline. - CI-job собирает образ при каждом push → новые зависимости видны на CI, а не в production.
- Deploy-runbook (§7) явно требует
R-16 — Атрибуция теряется при включении/выключении источников
- Описание: BRD-метрика «атрибуция каждого активного источника видна». При динамическом изменении набора enabled-источников (например, оператор временно выключил
ttrailsвgps_sources.yaml) клиент может продолжать показывать атрибуцию, потому что в БД уже есть треки сsources_jsonсодержащимttrails. - Вероятность / Влияние: Н / Н.
- Митигация:
- Атрибуция формируется на клиенте из
/api/gps-tracks/health.tracks_by_source(только source с tracks_count > 0). Если в БД осталисьttrailsзаписи — атрибуция корректно отображает. - Если source удалён + треки удалены —
tracks_by_sourceего не содержит → атрибуция корректно скрывается. - AC-15 проверяет.
- Атрибуция формируется на клиенте из
Сводная таблица
| ID | Риск | Вер. | Влияние | Класс | Статус |
|---|---|---|---|---|---|
| R-1 | Парсер ломается при смене HTML | В | С | Высокий | принят + per-source изоляция + алерт |
| R-2 | Ложные коллизии dedup | С | С | Средний | принят + метрика BRD + план отступления |
| R-3 | Pipeline повреждает БД | Н | В | Средний | atomic tx + ежедневный backup + rebuild за 6 ч |
| R-4 | Размер БД > 2 ГБ | Н | С | Низкий | GC + health + миграция на PostGIS |
| R-5 | IP mva154 банится | С | С | Средний | rate-limit + UA + backoff + отключение источника |
| R-6 | Pipeline деградирует API | Н | С | Низкий | cgroup limits + WAL + ночное окно |
| R-7 | Дублирование tile-утилит | С | Н | Низкий | принят + комментарии в коде + review-чеклист |
| R-8 | GeoJSON SLA на плотных bbox | С | Н | Низкий | limit + zoom-cutoff + R-tree |
| R-9 | Licensing-ADR не enforced | Н | В | Высокий | runtime-guard + Pydantic-валидация + тест |
| R-10 | Cache-clear доступен извне | Н | С | Низкий | nginx allow/deny |
| R-11 | Pipeline зависает | Н | С | Низкий | httpx timeout + max_tracks_per_run + (опц.) timeout cron |
| R-12 | UI несогласован после setStyle | Н | С | Низкий | паттерн ADR-004 + AC-12 |
| R-13 | Конфликт с ET-006 (GPX) | Н | С | Низкий | префикс + параллельные модели + AC-10 |
| R-14 | Конфликт с ET-007 (halo) | Н | С | Низкий | новые halo по тому же паттерну + AC-11 |
| R-15 | Зависимости pipeline | Н | Н | Низкий | CI-build + runbook |
| R-16 | Атрибуция теряется | Н | Н | Низкий | health-derived rendering |
Высокие классы:
- R-1 — операционный, ожидаемый для скрейп-источников; митигация — per-source изоляция и быстрое отключение через конфиг.
- R-9 — критический для legal compliance; митигация многослойная (Pydantic + runtime check + тест).
Блокирующих рисков нет. R-1 и R-9 требуют внимания разработки и code review, но не блокируют merge.
Эскалация
- arch:major-change — выставлен на ADR-005 (новая БД) и ADR-007 (новый сервис + cron). Требует архитектурного approve перед merge.
- back-to:analysis — не требуется. ТЗ полное, BRD ясный, ADR-010 и ADR-011 явно блокирующие до закрытия licensing review (это операционный pre-requisite, не дефект анализа).