316 lines
23 KiB
Markdown
316 lines
23 KiB
Markdown
---
|
||
type: tech-risks
|
||
work_item_id: ET-012
|
||
title: "Технические риски — ET-012: Снижение minzoom публичных треков до z5"
|
||
version: 1
|
||
status: approved
|
||
created_at: 2026-06-04
|
||
authors:
|
||
- "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 (контракты)
|