Files
enduro-trails/docs/work-items/ET-012/10-tech-risks.md
claude-bot c7d472023f
All checks were successful
CI / lint (push) Successful in 4s
CI / test (push) Successful in 7s
CI / build (push) Successful in 2s
architect(ET): auto-commit from architect run_id=73
2026-06-04 06:19:02 +00:00

23 KiB
Raw Blame History

type, work_item_id, title, version, status, created_at, authors
type work_item_id title version status created_at authors
tech-risks ET-012 Технические риски — ET-012: Снижение minzoom публичных треков до z5 1 approved 2026-06-04
agent:architect

Технические риски — ET-012

Технические риски этапа снижения нижнего порога видимости слоя публичных GPS-треков с z=8 до z=5. Бизнес-риски — в BRD §5 (R-1..R-10). Шкала: вероятность (Н/С/В) × влияние (Н/С/В).

R-T-1 — Размер MVT-тайла z=5 > 200 KB на реальных данных

  • Описание: На густонаселённых регионах (Москва, Урал) при росте БД до 5000+ треков фильтр length_m ≥ 10000 + limit=1500 может не сработать как страховка: 1500 треков × 200 байт после упрощения = ~300 KB до gzip, что близко к гейту M-8 (200 KB декомпрессировано на клиенте).
  • Вероятность / Влияние: С / С.
  • Митигация:
    • Архитектурное решение (ADR-016 §T-2): выбраны намеренно консервативные параметры (min_length 10 км, limit 1500) — это компромисс, а не «впритык». Запас 30-50% по M-8 при текущей БД (~500 треков ЦФО).
    • Хук на снижение: если PERF-Z5-01 или AC-10 покажут размер

      200 KB — снизить limit до 1000 в build_gps_mvt. Это правка одной константы, не требует архитектурного re-decide (см. ADR-016 §«Технический долг»).

    • Тесты: IT-Z5-01, IT-Z5-02 (REQ-F-11) — гейтируют размер на 50-200 треков; ручная проверка AC-10 — на реальной БД test-среды после деплоя.

R-T-2 — DP-tolerance 4 км на z5 «убивает» геометрию треков 10-15 км

  • Описание: Трек длиной 12 км с реальной траекторией (зигзаги лесных дорог) после Douglas-Peucker с tolerance 0.04° (~2.6 км по долготе на 55° с.ш.) превращается в 2-3 точки → визуально «прямая линия от А до Б». Пользователь думает, что трек прямой, и недооценивает сложность.
  • Вероятность / Влияние: В / Н.
  • Митигация:
    • Архитектурное решение (ADR-016 §T): на z5 трек ≤ 5 км схлопывается в прямую — это спецификация, не баг (BRD §5 R-2). На z5 пиксель ≈ 5 км, поэтому даже идеально точный зигзаг не видно глазом.
    • Спецификация поведения для пользователя: «z5 — общий обзор сети; для деталей зумьте до z=10+». Это документировано в BRD §2.2 и TRZ §6.
    • Тест: TC-UI-12-Z5-Q (качественный) — оператор глазами проверяет, что на z5 видны минимум 3 разных «нити» в кадре (AC-08).

R-T-3 — Линия 0.5 px на z5 невидима на 1×-DPR мониторе

  • Описание: Если бы оставили interpolate [..., 8, 1.0, ...], на z=5 MapLibre сэмплирует значение слева от первого стопа = 1.0, но после anti-aliasing на 1× мониторе линия «съедается» до ≤ 0.5px.
  • Вероятность / Влияние: С (без митигации — В) / Н.
  • Митигация:
    • Архитектурное решение (ADR-016 §L-B / REQ-F-05): явный стоп 5, 0.8 в _gpsLayerDef.paint['line-width']. 0.8 CSS-px = 1 физ.px на 1×-мониторе после округления GPU. Стоп 5, 1.8 в _gpsHaloDef (соотношение ~2.25×) — ореол не «съедает» линию.
    • Тесты: TC-UI-01-Z5 (Playwright), TC-UI-10-Z5-MOBILE (mobile viewport) — гейтируют видимость линии.

R-T-4 — bbox-запрос на z5 тянет всю БД (R-tree fallback to full scan)

  • Описание: Один z=5 тайл накрывает ~1250×1250 км по экватору, ~700×1250 на 55° с.ш. При БД 5000 треков по ЦФО — все 5000 строк имеют bbox внутри тайла, R-tree-индекс возвращает все ROWID, и далее SQLite делает SCAN по 5000 строк для подгрузки полей. На CI-runner это ≤ 100 мс, на mva154 — оценочно ≤ 150 мс (HDD-storage).
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение (ADR-016 §B): buffer 10% bbox не меняем в MVP — лишний 10%-запас погоды не делает при том, что основной фильтр — Python-фильтр по length_m после SELECT.
    • PERF-Z5-01 (REQ-F-13) — гейт; при росте БД и деградации — добавляем индекс на length_m DESC отдельным минорным патчем (см. ADR-016 §«Технический долг»).
    • Метрика M-6/M-7 — наблюдаем p95 в uvicorn.access после деплоя (см. 07-infra-requirements.md §7.1).

R-T-5 — LRU 1024 переполняется при walk-through-world

  • Описание: Если пользователь панорамирует карту на z=5 по всему миру, видит ~1024 уникальных тайла (z5 = 32×32). Серверный _gps_tile_cache ёмкостью 1024 при FIFO-вытеснении начинает выкидывать ранее запрошенные → повторный pan дёргает cold-build снова.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение (ADR-016 §C): размер LRU 1024 не меняем в MVP. На практике пользователь работает с регионом (ЦФО + соседние области = ~20-30 тайлов z5).
    • Метрика M-11 — гейт; если cache hit ratio < 80% — поднимаем до 2048 отдельным патчем.
    • Альтернатива (отложена): pre-render z=5 grid на диск при деплое (ADR-016 §P-B отклонён в MVP, но открыт для отдельного work-item).

R-T-6 — Hint «Зум 8+» забыт в HTML → пользователь видит линии и подсказку «увеличь зум»

  • Описание: В src/web/index.html строка <span ... id="public-tracks-zoom-hint">Зум 8+</span>. Если в ходе реализации правка REQ-F-07 потеряется (например, мердж-конфликт), у пользователя на z<5 будет hint «Зум 8+», который противоречит фактическому порогу 5.
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение (REQ-F-07): в HTML текст явно меняется на «Зум 5+». Логика показа в _syncGpsLayersVisibility автоматически использует GPS_TRACKS_MIN_ZOOM — порог переезжает автоматически.
    • Тесты: AC-05 (текст «Зум 5+»), TC-UI-04-HINT-OFF / TC-UI-05-HINT-ON (Playwright).
    • Acceptance check в 02-trz.md REQ-F-01 grep — гарантирует, что других вхождений константы со старым значением нет.

R-T-7 — Halo на спутнике на z5 «глушит» подложку

  • Описание: Если halo-line-width на z5 окажется слишком большим (например, по ошибке остался стоп 5, 4.0), белый ореол на спутниковой подложке закрывает большую часть рельефа в кадре.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение (REQ-F-06 / ADR-016 §L-B): halo z5 = 1.8 CSS-px; ограничено F-10 BRD ≤ 2 px. Соотношение к line-width (1.8 / 0.8 ≈ 2.25) — стандартное для трэйл-линий.
    • Тесты: TC-UI-11-Z5-SAT (Playwright со спутниковой подложкой); AC-17 (halo-width ≤ 2 px, halo не «глушит» подложку).

R-T-8 — Регрессия на z=8..11 из-за разделения tier z≤7 на z≤5/z=6/z=7

  • Описание: В новой tier-таблице (ADR-016 §«Решение» п.2) ранее единый блок z ≤ 7 → min_length=2000, limit=3000 разбит на z≤5: min_length=10000, limit=1500 | z=6: min_length=5000, limit=2000 | z=7: min_length=2000, limit=3000. Регрессия может проявиться, если при разбиении нечаянно поломан z=7 (например, ошибочный elif z <= 7 вместо elif z == 7).
  • Вероятность / Влияние: С / С.
  • Митигация:
    • Архитектурное решение (REQ-F-03): код-сниппет в TRZ §3.3 точно указывает структуру if z <= 5 / elif z == 6 / elif z == 7 / elif z <= 9 / ....
    • Регрессионные тесты: UT-Z7-01, UT-Z8-01, UT-Z12-01 (REQ-F-09), IT-REGRESS-Z8-01, IT-REGRESS-Z10-01 (REQ-F-12), AC-06.
    • Code review проверяет if-elif-цепочку построчно.

R-T-9 — Cache poisoning: после deploy старые тайлы z8-z11 остались с прежней tier-логикой

  • Описание: _gps_tile_cache — in-memory FIFO; при перезапуске app он очищается автоматически. Но если оператор docker compose restart app не сделал, а только docker compose up -d --no-deps app пересобрал образ → новый процесс стартует с пустым кэшем, всё ок. Риск только при использовании docker compose exec или hot-reload (не наш случай в проде).
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение: docker compose up -d --no-deps app в 07-infra-requirements.md §6.2 шаг 2 — пересоздаёт контейнер, кэш пустой.
    • Подстраховка: POST /api/gps-tracks/cache/clear в шаге 4 (на случай race conditions).
    • Браузерный кэш: MapLibre LRU при reload очищается; Cache-Control: max-age=300 ограничивает максимум 5 минут «застрявших» тайлов в браузерном кэше.

R-T-10 — _simplify_coords падает с ValueError при пустом coords на z=5

  • Описание: Существующий код: if len(coords) < 3: return coords — защита от пустых/коротких массивов. После добавления tier для z5 проверка остаётся. Но: shapely.LineString(coords).simplify(0.04, ...) при tolerance ≥ длины трека вернёт LineString из 2 точек (концы) или пустую коллекцию. Если результат пустой — fallback return coords возвращает оригинал.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение: существующий fallback return result if len(result) >= 2 else coords (mvt.py:50) остаётся. Покрытие тестом UT-SIMP-Z5-02 (зигзаг 100 точек → 2 точки = валидный LineString).
    • Дополнительный тест (рекомендуется в pull request): _simplify_coords([(37.0, 55.0), (37.001, 55.001)], 5) → возвращает оригинал (2 точки).

R-T-11 — Размер MVT z=5 = 0 байт на регионе без длинных треков

  • Описание: После фильтра length_m ≥ 10000 в регионах с только короткими треками (например, лесопарки внутри города) тайл z=5 содержит 0 фич → возвращается b"". _row_to_geojson_feature / build_gps_mvt возвращают пустой protobuf, что MapLibre корректно интерпретирует как «фич нет».
  • Вероятность / Влияние: С / Н (это ожидаемое поведение).
  • Митигация:
    • Архитектурное решение: на z=5 в регионе без длинных треков — пусто. Это специфицировано в BRD §2.2 и AC-03 (требуется БД с ≥ 50 треков ≥ 10 км по ЦФО).
    • Тест: IT-Z5-03 (REQ-F-11) — тайл z=5 за пределами региона возвращает 200 с пустым телом.
    • UX: пользователь видит «пустую карту» на z=5, но hint не показывается (zoom ≥ 5); если пользователь зумит до z=8, появляются короткие треки. Естественная семантика.

R-T-12 — Старый клиент (закэшированный в браузере) делает запросы только на z≥8

  • Описание: Пользователь с открытой вкладкой неделю назад имеет закэшированный gps_tracks.js со старым GPS_TRACKS_MIN_ZOOM = 8. После деплоя при reload gps_tracks.js обновится (если есть ?v=... versioning) или дотянется service-worker'ом. Service worker — не настроен в MVP (PH-9 не реализована).
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение: src/web/index.html загружает gps_tracks.js напрямую (без SW). При reload браузер дёрнет последнюю версию (если nginx отдаёт нужные cache-headers). Если нет — пользователь сделает Ctrl+F5 после очередного апа.
    • Backwards compat: старый клиент с MIN_ZOOM=8 продолжает работать; он просто не запрашивает z=5..7. Никаких 4xx-ответов нет (REQ-F-15 — контракт не сломан).
    • Митигация в долгую: PWA / SW (PH-9, отдельный work-item) введёт правильную inval-стратегию.

R-T-13 — DDoS на новый z=5 endpoint (бот ходит по 32×32 z5 grid)

  • Описание: Поскольку endpoint без auth и без rate-limit, скрипт-крулер может запросить все 1024 тайла z=5 за минуту → 1024 × ~200 мс build = ~3.5 минуты CPU на сервере. Не убийственно, но заметно.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение: rate-limit не вводим в MVP (см. 07-infra-requirements.md §3.2). LRU кэш съест второй проход — cold пройдёт один раз.
    • Мониторинг: в первую неделю после деплоя оператор смотрит nginx access.log на аномалии (см. 07-infra-requirements.md §7.1).
    • Эскалация: если обнаружится паттерн — slowapi-middleware (отдельный DevOps-task).

R-T-14 — Конфликт с halo при переключении spectator/satellite на z5

  • Описание: При переключении подложки applyBaseLayer() (ET-007) должен корректно показать/скрыть halo для GPS-треков. На z=5 halo активен (zoom ≥ GPS_TRACKS_MIN_ZOOM AND zoom < GPS_TRACKS_ZOOM_CUTOFF AND base === 'satellite'). Если в applyGpsHaloVisibility есть hardcoded порог z≥8 — будет расхождение.
  • Вероятность / Влияние: Н / Н.
  • Митигация:
    • Архитектурное решение: в gps_tracks.js существующая логика _syncGpsLayersVisibility / applyGpsHaloVisibility использует GPS_TRACKS_MIN_ZOOM как константу — порог переезжает автоматически (verified by grep в TRZ §3 REQ-F-01).
    • Тесты: TC-UI-11-Z5-SAT (Playwright со спутниковой подложкой), AC-17.

R-T-15 — Performance тест PERF-Z5-01 нестабилен на CI

  • Описание: PERF-Z5-01 (REQ-F-13) измеряет p95 build_gps_mvt z=5 при 500 треках. CI-runner может иметь cold I/O в первом прогоне → fail. Это flaky-тест.
  • Вероятность / Влияние: С / Н.
  • Митигация:
    • Архитектурное решение: PERF-тест с маркером @pytest.mark.perf запускается отдельным джобом (TRZ §3.13) — не блокирует merge. Логируется в 13-test-report.md для тренд-анализа.
    • Дизайн теста: делать 10 повторов, отбрасывать первый (warmup) — стандартный паттерн для micro-benchmark'ов.
    • Gate: avg ≤ 200 мс, p95 ≤ 500 мс (gentle).

R-T-16 — Конфигурация nginx gzip для application/x-protobuf пропала

  • Описание: Если nginx config был перезатёрт (например, после переустановки) и application/x-protobuf не в gzip_types, размер MVT z5 пойдёт unzipped (~80 KB на тайл) → мобильный трафик и latency растут.
  • Вероятность / Влияние: Н / С.
  • Митигация:
    • Smoke-проверка в 07-infra-requirements.md §6.2 шаг 3: curl -I смотрит на content-encoding: gzip после деплоя.
    • Если gzip нет — операт восстанавливает nginx config из git (infra/nginx/openclaw.conf или эквивалент).

Сводная таблица

# Риск Вер Влиян Митигация (тип)
R-T-1 Размер MVT z5 > 200 KB С С Архитектурное (tier T-2) + гейт-тест
R-T-2 DP-tolerance ломает геометрию коротких треков В Н Спецификация (z5 = обзор)
R-T-3 Линия невидима на 1×-DPR С Н Архитектурное (line-width стоп 0.8)
R-T-4 bbox-запрос z5 тянет всю БД С Н Гейт-метрика + index-watch flag
R-T-5 LRU 1024 переполнение Н Н Метрика M-11; capacity hook
R-T-6 Hint «Зум 8+» забыт С Н grep-проверка + UI-тест
R-T-7 Halo «глушит» подложку Н Н Архитектурное (1.8 px) + UI-тест
R-T-8 Регрессия z8-z11 из-за tier-rewrite С С Снимок tier в TRZ + регресс-тесты
R-T-9 Cache poisoning после deploy Н Н Procedure (cache clear) в infra
R-T-10 _simplify_coords падает на пустых Н Н Existing fallback + unit-тест
R-T-11 Пустой MVT в регионе без длинных треков С Н Specified behavior + IT-Z5-03
R-T-12 Старый клиент в кэше браузера С Н Backwards-compat (контракт)
R-T-13 DDoS на новый z=5 endpoint Н Н LRU защищает; rate-limit отложен
R-T-14 Halo не sync на z5 Н Н Existing-pattern reuse + UI-тест
R-T-15 PERF-тест flaky на CI С Н Marker @perf, отдельный джоб
R-T-16 nginx gzip пропал Н С Smoke-проверка после деплоя

Связанные документы

  • 01-brd.md §5 Бизнес-риски R-1..R-10 (часть пересекается)
  • 02-trz.md §3 REQ-F-09..F-14 (тесты), §4 NFR
  • 06-adr/ADR-016-z5-tiling-policy.md §«Решение», §«Последствия»
  • 07-infra-requirements.md §3 (rate-limit), §6 (procedure), §7 (мониторинг)
  • 08-data-requirements.md §3.4 (индексы), §5 (контракты)