architect(ET): auto-commit from architect run_id=73
This commit is contained in:
@@ -19,3 +19,4 @@
|
||||
| ADR-013 | Активация EnduroRussia + Wikiloc — конфиг-only изменения поверх pipeline ET-008 (URL-fix, новая запись wikiloc, регионы, стили, атрибуция) | accepted | 2026-06-01 | [ET-009](../../work-items/ET-009/06-adr/ADR-013-source-activation.md) |
|
||||
| ADR-014 | GPX-download эндпоинт публичного трека: `xml.etree.ElementTree`-builder + fetch+Blob на клиенте | accepted | 2026-06-03 | [ET-011](../../work-items/ET-011/06-adr/ADR-014-gpx-download-endpoint.md) |
|
||||
| ADR-015 | Политика реэкспорта публичных треков: per-source `download_allowed` в `gps_sources.yaml`, default-deny (whitelist `osm` для MVP) | accepted | 2026-06-03 | [ET-011](../../work-items/ET-011/06-adr/ADR-015-source-redistribution-policy.md) |
|
||||
| ADR-016 | Снижение minzoom публичных GPS-треков до z5: калибровка существующих tier-таблиц `build_gps_mvt`/`_simplify_coords`, on-demand MVT остаётся, без heat-map/clustering | accepted | 2026-06-04 | [ET-012](../../work-items/ET-012/06-adr/ADR-016-z5-tiling-policy.md) |
|
||||
|
||||
305
docs/work-items/ET-012/06-adr/ADR-016-z5-tiling-policy.md
Normal file
305
docs/work-items/ET-012/06-adr/ADR-016-z5-tiling-policy.md
Normal file
@@ -0,0 +1,305 @@
|
||||
---
|
||||
type: adr
|
||||
work_item_id: ET-012
|
||||
adr_id: ADR-016
|
||||
title: "ADR-016: Снижение minzoom публичных GPS-треков до z5 — калибровка существующих tier-таблиц, on-demand MVT остаётся, без heat-map/clustering"
|
||||
status: accepted
|
||||
created_at: 2026-06-04
|
||||
updated_at: 2026-06-04
|
||||
authors:
|
||||
- "agent:architect"
|
||||
supersedes: []
|
||||
superseded_by: []
|
||||
labels:
|
||||
- "ET-012:tiling"
|
||||
- "minor-change"
|
||||
---
|
||||
|
||||
# ADR-016 — Политика отдачи треков на z5-z7
|
||||
|
||||
## Статус
|
||||
|
||||
**Accepted.** Архитектурное решение для ET-012.
|
||||
|
||||
Это **калибровка** (а не пересмотр) стратегии, заложенной в ADR-008.
|
||||
BRD §6 «Документация» допускает отсутствие отдельного ADR для этой
|
||||
задачи, поскольку tier-структура `build_gps_mvt`/`_simplify_coords`
|
||||
изначально расширяема. ADR оформляется ради единого индекса
|
||||
архитектурных решений и чтобы зафиксировать **причины отклонения
|
||||
альтернатив** (heat-map, pre-rendering, snap-to-h3) — иначе они
|
||||
вернутся в обсуждение в следующем work-item.
|
||||
|
||||
## Контекст
|
||||
|
||||
### Текущее состояние (после ET-008 / ADR-008 / ET-009)
|
||||
|
||||
- ADR-008 §4-5 закрепил **двухрежимную отдачу**:
|
||||
- z ∈ [`GPS_TRACKS_MIN_ZOOM`, `GPS_TRACKS_ZOOM_CUTOFF`) — MVT через
|
||||
`GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt` + серверный LRU(1024);
|
||||
- z ≥ `GPS_TRACKS_ZOOM_CUTOFF` (= 12) — GeoJSON через
|
||||
`GET /api/gps-tracks?bbox=…`;
|
||||
- z < `GPS_TRACKS_MIN_ZOOM` — слой полностью скрыт (защита от
|
||||
шторма запросов).
|
||||
- `GPS_TRACKS_MIN_ZOOM = 8` (хардкод в `src/web/gps_tracks.js:8` и
|
||||
`gps-tracks-tiles.minzoom`).
|
||||
- `build_gps_mvt` (`src/api/gps_tracks/mvt.py`) уже содержит
|
||||
zoom-aware tier-таблицу `min_length_m`/`limit` (z≤7, z≤9, z≤11, z≥12).
|
||||
- `_simplify_coords` уже содержит tier по Douglas-Peucker tolerance
|
||||
(z≥12: без упрощения; z≥10: 0.0005°; z≥8: 0.002°; иначе 0.008°).
|
||||
- БД `data/gps_tracks.sqlite` — порядка сотен треков сейчас, прогноз
|
||||
до 5000 за горизонт года, индексы по `min_lon/max_lon/min_lat/max_lat`
|
||||
(BRD §2.1, TRZ §2).
|
||||
|
||||
### Что хочет ET-012
|
||||
|
||||
Снизить нижний порог видимости слоя с z8 до z5, чтобы при первом
|
||||
открытии карты (которая по умолчанию на обзорном зуме) пользователь
|
||||
сразу видел общее покрытие сети треков (BRD §2.2).
|
||||
|
||||
Архитектурный вопрос: **как заставить on-demand MVT работать
|
||||
приемлемо на z5-z7 без введения новых сервисов и без потери
|
||||
читаемости.** «Просто понизить константу» — недостаточно: на z5 один
|
||||
тайл накрывает ~1250×1250 км, и без агрессивной фильтрации/упрощения:
|
||||
|
||||
- размер MVT может перевалить 1 MB (R-1);
|
||||
- DP-tolerance 0.008° (≈800 м) превратит трек 30 км в зигзаг из 30
|
||||
точек, что бессмысленно при пиксельном размере карты ~5 км/px (R-2);
|
||||
- линия `0.5 px` на z5 будет невидима (R-3);
|
||||
- bbox-запрос рискует прочитать треки всей страны без LIMIT (R-4);
|
||||
- LRU из 1024 тайлов теоретически может вытесняться при walk-through
|
||||
world (R-6).
|
||||
|
||||
Все эти риски — в BRD §5; нужно **архитектурно их закрыть** до
|
||||
реализации, а не разруливать в коде ad-hoc.
|
||||
|
||||
## Рассмотренные варианты
|
||||
|
||||
### Вариант P (Pipeline) — как готовить тайлы z5-z7
|
||||
|
||||
- **P-A — on-demand build с тем же LRU 1024** (выбран):
|
||||
- Тайлы z=5/6/7 строятся в `build_gps_mvt(rows, z, x, y)` так же,
|
||||
как z=8..z=11. Кэш общий.
|
||||
- Никаких новых сервисов / cron / volume. Никакой инвалидации
|
||||
поверх существующей `POST /api/gps-tracks/cache/clear` (ADR-008
|
||||
§7) не нужно.
|
||||
- Cold-time дешёвый: один SELECT по R-tree-индексу + Python-loop
|
||||
с генерализацией. На БД ≤ 5000 треков по ЦФО — < 200 мс (PERF-Z5-01).
|
||||
- **P-B — pre-generate всю сетку z=5..z=7 на диск** (Tilelive-стиль).
|
||||
Отклонён:
|
||||
- z5: 32×32 = 1024 тайла; z6: 4096; z7: 16384 — суммарно ~21k.
|
||||
После gzip ~1.5 MB / 6 MB / 24 MB соответственно. Не критично
|
||||
по диску, но: ломает существующий cache-invalidation (нужно
|
||||
удалять файлы, а не `_tile_cache.clear()`), вводит новый
|
||||
pre-warm step после каждого `gps-collector` run.
|
||||
- Усложняет deployment (volume mount, fs perms).
|
||||
- Не даёт ничего сверх LRU при текущей нагрузке (пара пользователей
|
||||
в test). При росте нагрузки — возврат к рассмотрению как
|
||||
отдельный work-item.
|
||||
- **P-C — внешний tile server (Tegola/Martin/tilemaker)**. Отклонён
|
||||
как и в ADR-008 §T-C: новый сервис, новый артефакт деплоя; не
|
||||
оправдано размером данных.
|
||||
|
||||
### Вариант T (Tier values) — на каком уровне обрезать на z5-z6
|
||||
|
||||
Цели:
|
||||
- M-6 (p95 build ≤ 500 мс на z5);
|
||||
- M-8 (размер MVT z5 ≤ 200 KB);
|
||||
- M-9 (читаемость z5 — ≥ 3 различимых линий в кадре по ЦФО).
|
||||
|
||||
Кандидаты, рассмотренные на берегу:
|
||||
|
||||
| Tier | z5 min_len | z5 limit | z6 min_len | z6 limit | Заключение |
|
||||
|--------|-----------:|---------:|-----------:|---------:|------------|
|
||||
| T-1 | 20000 m | 500 | 10000 m | 1000 | Слишком жёстко: при ЦФО получаем ~10-15 треков в кадре, M-9 проходит, но «обзор сети» теряется — большая часть треков невидима. |
|
||||
| T-2 (**выбран**) | 10000 m | 1500 | 5000 m | 2000 | Соответствует BRD/TRZ REQ-F-03. На ЦФО (БД ~500 длинных треков) даёт ~50-80 фич в тайле z5, ~150 в z6. Размер до gzip ~80-100 KB; после nginx-gzip ~30 KB. M-6, M-8, M-9 проходят с запасом. |
|
||||
| T-3 | 5000 m | 3000 | 2000 m | 3000 | Не оставляет запаса по M-8: при 5000 треков размер MVT z5 может вылезти за 200 KB при «густой» области. Резерва на рост БД нет. |
|
||||
|
||||
**Tier T-2 — компромисс «обзор сети» × «гарантированный лимит»**.
|
||||
|
||||
`tolerance` для DP подобрана так, чтобы trace ≤ 5 км на z5
|
||||
схлопывалось в прямую (tolerance ~4 км / 0.04° долготы на 55° с.ш.).
|
||||
Для z6 tolerance = 0.018° (~2 км) — позволяет видеть крупные изгибы
|
||||
длинных треков (TRZ §3.4 REQ-F-04).
|
||||
|
||||
### Вариант L (Layer style) — как делать линию читаемой на z5
|
||||
|
||||
- **L-A — статичный `line-width: 1px`** (как было для z≥8). Отклонён:
|
||||
на retina-дисплеях 1 CSS-pixel = 2-3 physical pixels, на z5 это
|
||||
выглядит как «жирная нить»; на 1×-дисплеях 1px после anti-aliasing
|
||||
частично «съедается».
|
||||
- **L-B — интерполяция `interpolate linear zoom 5 0.8 8 1.0 ...`**
|
||||
(выбран, REQ-F-05):
|
||||
- z=5: 0.8 CSS-px → 1 физ.px на 1×, 1.6 на 2×, 2.4 на 3×. Видно
|
||||
везде.
|
||||
- z=8: 1.0 CSS-px (= как было).
|
||||
- Halo (REQ-F-06): z=5: 1.8 px; соотношение ~2.25× к основной
|
||||
линии → ореол не «съедает» линию.
|
||||
- **L-C — Switch на pattern/dash на z5** (тонкая прерывистая линия,
|
||||
как «маршрут на карте мира»). Отклонён: визуально несовместимо с
|
||||
z6+; пользователь будет видеть «прыжок стиля» при zoom-in.
|
||||
|
||||
### Вариант B (Buffer) — bbox-padding на z5
|
||||
|
||||
В endpoint `gps_tile` сейчас bbox расширяется на 10% при запросе к БД
|
||||
(ADR-008 §8) — это страховка от «обрезанных» треков на границе тайла.
|
||||
На z5 10% bbox-расширение = ~125 км в каждую сторону, что:
|
||||
|
||||
- **избыточно** для z5 — соседний тайл всё равно нарисует пограничный
|
||||
трек как часть собственного MVT;
|
||||
- **не вредит** существенно — Spatialite-R-tree всё равно фильтрует
|
||||
по min/max lon/lat быстро.
|
||||
|
||||
Решение: **buffer не меняем в MVP**. Если PERF-Z5-01 покажет
|
||||
деградацию — снизим до 5% точечно для z≤6 в отдельном минорном
|
||||
изменении (TRZ §6, R-5).
|
||||
|
||||
### Вариант C (Cache size) — нужно ли увеличивать LRU
|
||||
|
||||
Сейчас `_GPS_TILE_CACHE_MAX = 1024`.
|
||||
|
||||
- На z=5 в мире 32×32=1024 уникальных тайлов; пользователь на практике
|
||||
видит 4-8 одновременно. Walk-through-world попросит ~50 уникальных.
|
||||
- На z=5..z=11 совместно при «обычном» использовании в кадре
|
||||
одновременно держится ~10-20 тайлов.
|
||||
- **Решение: не трогаем 1024 в MVP** (TRZ §6, R-6). Поднимем до 2048
|
||||
отдельным минорным изменением, если PERF-метрика M-11 даст cache
|
||||
hit < 80%.
|
||||
|
||||
### Вариант H (Heat-map for z3-z4) — что показывать ниже z5
|
||||
|
||||
- **H-A — heat-map / clustering на z3-z4** (Wikiloc/Komoot-стиль).
|
||||
**Отклонён из ET-012** (BRD §3 Out of scope):
|
||||
- Требует серверную агрегацию (например, h3-cell counts или
|
||||
grid-density-precompute).
|
||||
- Требует новый UI-слой (raster heatmap-source или CircleLayer с
|
||||
weight-based radius).
|
||||
- Делается отдельным work-item.
|
||||
- **H-B — оставить «слой пуст, но hint показывает «Зум 5+»** (выбран,
|
||||
REQ-F-07):
|
||||
- Существующая логика `_syncGpsLayersVisibility` уже показывает
|
||||
hint при `zoom < GPS_TRACKS_MIN_ZOOM`. После понижения константы
|
||||
hint появляется при z<5, что и желательно: на z3-z4 у пользователя
|
||||
есть явное объяснение, почему «пусто».
|
||||
|
||||
## Решение
|
||||
|
||||
Принимается **P-A + T-2 + L-B + B(no-change) + C(no-change) + H-B**:
|
||||
|
||||
1. **On-demand MVT** на всех зумах [5..11]; LRU и
|
||||
cache-invalidation — без изменений (ADR-008 §6-7 наследуется).
|
||||
|
||||
2. **Tier-таблица в `build_gps_mvt`**:
|
||||
|
||||
```python
|
||||
if z <= 5: min_length_m = 10000; limit = 1500
|
||||
elif z == 6: min_length_m = 5000; limit = 2000
|
||||
elif z == 7: min_length_m = 2000; limit = 3000
|
||||
elif z <= 9: min_length_m = 0; limit = 8000
|
||||
elif z <= 11: min_length_m = 0; limit = 15000
|
||||
else: min_length_m = 0; limit = 25000
|
||||
```
|
||||
|
||||
Цифры выводятся из M-6/M-8/M-9: предполагаемый максимум
|
||||
1500 фич × 200 байт ≈ 300 KB до gzip → ≈ 80 KB после nginx-gzip.
|
||||
|
||||
3. **Tier-таблица в `_simplify_coords`**:
|
||||
|
||||
```python
|
||||
z>=12: return coords # без упрощения
|
||||
z>=10: tolerance = 0.0005 # ~50 м
|
||||
z>=8: tolerance = 0.002 # ~200 м
|
||||
z==7: tolerance = 0.008 # ~800 м (как раньше)
|
||||
z==6: tolerance = 0.018 # ~2 км
|
||||
else: tolerance = 0.04 # ~4 км (z5 и ниже)
|
||||
```
|
||||
|
||||
На 55° с.ш. 0.04° долготы ≈ 2.6 км — оптимум «одна точка на
|
||||
пиксель» при размере пикселя z5 ≈ 5 км/px по экватору.
|
||||
|
||||
4. **Клиент**:
|
||||
- `GPS_TRACKS_MIN_ZOOM = 5` в `src/web/gps_tracks.js:8`.
|
||||
`gps-tracks-tiles.minzoom` подхватит автоматически (REQ-F-01..F-02).
|
||||
- `_gpsLayerDef.paint['line-width']` — расширить интерполяцию
|
||||
стопом z=5 → 0.8 (REQ-F-05).
|
||||
- `_gpsHaloDef.paint['line-width']` — стопом z=5 → 1.8 (REQ-F-06,
|
||||
R-8/R-10).
|
||||
- `#public-tracks-zoom-hint` — текст «Зум 5+» (REQ-F-07).
|
||||
Логика показа `(enabled && zoom < GPS_TRACKS_MIN_ZOOM)` не
|
||||
меняется — порог переехал автоматически.
|
||||
|
||||
5. **Backend endpoint** `get_gps_tile` — без изменений; валидация
|
||||
`0 ≤ z ≤ 22` уже пропускает z=5..7 (REQ-F-08).
|
||||
|
||||
6. **Buffer (10% bbox) и `_GPS_TILE_CACHE_MAX = 1024`** — без
|
||||
изменений в MVP. Оба пункта остаются как hooks для отдельного
|
||||
мелкого изменения, если PERF-/M-метрики не сойдутся (TRZ §6).
|
||||
|
||||
7. **z3-z4** — слой остаётся скрытым, hint объясняет. Heat-map —
|
||||
отдельный work-item.
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Минимальная инвазивность: 1 константа на клиенте + 2 переписанных
|
||||
блока на сервере + 2 правки стилей + 1 правка hint. Никаких новых
|
||||
модулей, файлов, сервисов, миграций, env, секретов, портов.
|
||||
- ADR-008 двухрежимная стратегия (MVT z<12, GeoJSON z≥12) не
|
||||
затрагивается — z12+ ведёт себя как прежде, регрессии нет
|
||||
(AC-07, IT-REGRESS-Z8-01/Z10-01).
|
||||
- Тонкая настройка через числовые tier-параметры — изменяется в одной
|
||||
функции; будущая корректировка («z=5 → limit=1000 для роста БД»)
|
||||
делается в 5 минут без архитектурных правок.
|
||||
- Существующий cache-clear-hook (`POST /api/gps-tracks/cache/clear`)
|
||||
автоматически очищает и тайлы z5-z7 после прогона pipeline'а
|
||||
(ADR-007 §7) — никакая дополнительная инвалидация не нужна.
|
||||
|
||||
### Отрицательные / ограничения
|
||||
|
||||
- **Эффективный «жёсткий cutoff» по длине трека на z5-z6.** Треки
|
||||
короче 10 км невидимы на z5, короче 5 км — на z6. Пользователь не
|
||||
увидит «полные грунтовые километры» в обзоре — только магистральные
|
||||
трассы. Принято: для z5-z6 «общее покрытие сети» = «магистральная
|
||||
сеть» (BRD §2.2).
|
||||
- **Hint «Зум 5+» появляется только при z<5**, что эффективно — только
|
||||
для z ∈ {0..4}. На самом верхнем зуме «обзор континента» (z3) у
|
||||
пользователя пусто. Митигация — heat-map в отдельном work-item.
|
||||
- **Размер LRU 1024 теоретически переполняется при walk-through-world
|
||||
z5+z6 одновременно** (1024 + 4096 уникальных тайлов). На практике
|
||||
пользователь работает с регионом; rotate работает. Митигация
|
||||
отложена (R-6).
|
||||
- **Buffer 10% bbox на z5 = 125 км запас** — формально избыточен, но
|
||||
не вредит: R-tree-фильтр быстрый, лишние треки отрезает Python-loop
|
||||
по `min_length`. Митигация отложена (R-5).
|
||||
- **DP-tolerance ~4 км на z5 может «выпрямить» зигзагообразный трек
|
||||
в прямую.** Это норма для обзорного зума (BRD §5, R-2): трек 5 км
|
||||
→ отрезок. Качественная проверка по TC-UI-12-Z5-Q.
|
||||
|
||||
### Технический долг
|
||||
|
||||
- Текущая tier-таблица в `build_gps_mvt` — копипаста if-elif. Если
|
||||
появится третий MVT-источник (например, шлагбаумы ET-PH-7) — вынести
|
||||
tier-функцию в shared util `mvt_tiers.py`. Не блокер MVP, отмечено
|
||||
как наследие ADR-005 §8 / ADR-008 §«Технический долг».
|
||||
- При росте БД до десятков тысяч треков может понадобиться вторичный
|
||||
индекс на `length_m` для серверной сортировки/фильтрации (R-4); пока
|
||||
индексы по bbox + Python-фильтр справляются. Отложено.
|
||||
|
||||
## Классификация изменения
|
||||
|
||||
**Minor change.** ET-012 — калибровка существующей tier-структуры
|
||||
ADR-008. Новых сервисов, БД, очередей, HTTP-эндпоинтов, env, портов,
|
||||
секретов, миграций не добавляется. Контракт API не меняется
|
||||
(REQ-F-15). `arch:major-change` не требуется.
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `docs/work-items/ET-012/01-brd.md` §3 Scope, §5 Риски R-1..R-10, §6 Зависимости
|
||||
- `docs/work-items/ET-012/02-trz.md` REQ-F-01..F-08, §4 NFR, §6 Открытые вопросы
|
||||
- `docs/work-items/ET-012/03-acceptance-criteria.md` AC-01..AC-21
|
||||
- `docs/work-items/ET-012/07-infra-requirements.md` (этот пакет)
|
||||
- `docs/work-items/ET-012/08-data-requirements.md` (этот пакет)
|
||||
- `docs/work-items/ET-012/10-tech-risks.md` (этот пакет)
|
||||
- `docs/work-items/ET-008/06-adr/ADR-005-storage-schema.md` §8 (общий tile-utility, наследие)
|
||||
- `docs/work-items/ET-008/06-adr/ADR-007-pipeline-architecture.md` §7 (cache-clear hook)
|
||||
- `docs/work-items/ET-008/06-adr/ADR-008-tile-vs-geojson-strategy.md` (родительская стратегия отдачи)
|
||||
236
docs/work-items/ET-012/07-infra-requirements.md
Normal file
236
docs/work-items/ET-012/07-infra-requirements.md
Normal file
@@ -0,0 +1,236 @@
|
||||
---
|
||||
type: infra-requirements
|
||||
work_item_id: ET-012
|
||||
title: "Инфраструктурные требования — ET-012: Снижение minzoom публичных треков до z5"
|
||||
version: 1
|
||||
status: approved
|
||||
created_at: 2026-06-04
|
||||
authors:
|
||||
- "agent:architect"
|
||||
---
|
||||
|
||||
# Инфраструктурные требования — ET-012
|
||||
|
||||
## 1. Резюме
|
||||
|
||||
ET-012 — **calibration only**. Меняются три файла исходного кода
|
||||
(`src/api/gps_tracks/mvt.py`, `src/web/gps_tracks.js`,
|
||||
`src/web/index.html`) + добавляются тесты. Инфраструктура **не
|
||||
меняется**:
|
||||
|
||||
- 0 новых docker-сервисов;
|
||||
- 0 изменений в `Dockerfile`;
|
||||
- 0 изменений в `docker-compose.yml`;
|
||||
- 0 новых файлов БД, миграций, индексов;
|
||||
- 0 новых cron-записей;
|
||||
- 0 новых env / секретов / API-ключей;
|
||||
- 0 новых исходящих HTTPS-соединений;
|
||||
- 0 новых портов;
|
||||
- 0 изменений в nginx (новый minzoom прозрачен для прокси).
|
||||
|
||||
Эскалация: **minor change** (см. ADR-016 §«Классификация изменения»).
|
||||
|
||||
## 2. Контейнеры и сервисы
|
||||
|
||||
| Аспект | Требование |
|
||||
|----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Новый сервис | **Нет** |
|
||||
| Изменения `Dockerfile` | **Нет** |
|
||||
| Изменения `docker-compose.yml` | **Нет** |
|
||||
| Перезапуск `app` после деплоя | Нужен — `docker compose up -d --no-deps app` (≈ 5 сек простоя). Подхватывает новую tier-таблицу в `build_gps_mvt`, новый `_simplify_coords`, обновлённые `src/web/*.js` / `*.html` |
|
||||
| Перезапуск `gps-collector` | Не нужен — pipeline не затронут (collector только пишет в БД, отдачей не занимается) |
|
||||
| Очистка серверного MVT-кэша после деплоя | Нужна — `_gps_tile_cache` старых тайлов z5-z7 не существует (раньше слой был скрыт), но кэш z8-z11 надо инвалидировать через `POST /api/gps-tracks/cache/clear` (см. §6.2) |
|
||||
| Очистка клиентского кэша / Service Worker | Не нужно — `gps_tracks.js` подгружается с `?v=...` версионным query-параметром (см. `src/web/index.html` загрузка модулей); пользователь получит обновлённый клиент при reload |
|
||||
|
||||
### 2.1 Зависимости между сервисами
|
||||
|
||||
Без изменений vs ET-008/ET-009/ET-011. Те же зависимости:
|
||||
|
||||
- `app` → файл `/app/data/gps_tracks.sqlite` (read-only при отдаче,
|
||||
read/write только из `gps-collector`).
|
||||
- `gps-collector` → тот же файл (offline pipeline, не затрагивается).
|
||||
- `nginx (host)` → `app:8000` через docker-network bridge.
|
||||
|
||||
## 3. Сеть
|
||||
|
||||
| Аспект | Требование |
|
||||
|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Новые входящие порты | **Нет** |
|
||||
| Изменения nginx | **Нет** (тот же `location /enduro/api/gps-tracks/tiles/{z}/{x}/{y}.mvt`; новые z=5/6/7 — это просто другие значения существующего path-параметра) |
|
||||
| nginx gzip для MVT | Должен быть включён в `mime.types`/`gzip_types` для `application/x-protobuf`. Это уже было сделано в ET-008. **Проверить при деплое** (см. §6.2 шаг 3) |
|
||||
| Кэш-заголовки на MVT | Без изменений — endpoint отдаёт `Cache-Control: public, max-age=300` (как было). На клиенте MapLibre LRU + браузер-кэш используют это |
|
||||
| Новые исходящие соединения | **Нет** — никаких внешних API не дёргается, всё локально |
|
||||
| CORS | Без изменений; middleware уже отдаёт `Access-Control-Allow-Origin: *` для всего `/api/` |
|
||||
|
||||
### 3.1 Ingress traffic — оценка дельты
|
||||
|
||||
Размер MVT-тайла z=5 ≤ 200 KB до gzip (M-8), после nginx gzip ~50-70 KB.
|
||||
|
||||
Сценарий «пользователь открыл карту, увидел z5, попанил по ЦФО»:
|
||||
|
||||
- Тайлов в кадре одновременно: ~6-10 на z5.
|
||||
- Уникальных за сессию (~5 минут pan): 20-30.
|
||||
- Итого ingress: 20-30 × 70 KB = ~1.5-2 MB на сессию **сверх** того,
|
||||
что было раньше (раньше на z5 запросов не было вообще — слой был
|
||||
скрыт).
|
||||
|
||||
Это допустимая дельта — uplink mva154 ≥ 100 Mbps по DuckDNS, при
|
||||
10 одновременных пользователях пик ≈ 15 Mbps входящего трафика,
|
||||
≈ 80 Mbps уходящего (тайлы клиенту).
|
||||
|
||||
### 3.2 Rate-limit на endpoint
|
||||
|
||||
**Не вводим** в этой итерации (BRD §3 «out of scope»). Текущий
|
||||
`AbortController + 500 ms debounce` на клиенте (ADR-008 §D) и серверный
|
||||
LRU защищают от шторма.
|
||||
|
||||
Если в продакшене обнаружится бот / scraper, дёргающий весь z=5
|
||||
grid (1024 запроса) — добавляем `slowapi`-middleware отдельным
|
||||
DevOps-task'ом (out of ET-012).
|
||||
|
||||
## 4. Серверные ресурсы
|
||||
|
||||
| Аспект | Требование |
|
||||
|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| CPU `app` | Без изменений по архитектуре; рост нагрузки оценочно ≤ +5% при сценарии «один пользователь pan на z5» (генерация одного MVT ≤ 200 мс CPU). PERF-Z5-01 — гейт. |
|
||||
| RAM `app` | Без изменений. `_gps_tile_cache` ограничен 1024 записями × max 200 KB = 200 MB max. На практике средний размер MVT z5-z11 ≈ 50 KB → ≈ 50 MB в худшем случае |
|
||||
| Disk `app` | Без изменений. БД `gps_tracks.sqlite` не меняется; никаких новых файлов / volume |
|
||||
| CPU `gps-collector` | Без изменений (pipeline не затронут) |
|
||||
| RAM `gps-collector` | Без изменений |
|
||||
| Disk `gps-collector` | Без изменений |
|
||||
|
||||
### 4.1 LRU cache size
|
||||
|
||||
`_GPS_TILE_CACHE_MAX = 1024` — **не меняем** в MVP (ADR-016 §C).
|
||||
|
||||
Опционально можно поднять до 2048, если M-11 (cache hit ≥ 80%) не
|
||||
будет выполняться на test-среде после деплоя. Это маленький минорный
|
||||
патч (одна константа в `src/api/gps_tracks/mvt.py`), не требует
|
||||
архитектурного решения.
|
||||
|
||||
## 5. Конфигурация и секреты
|
||||
|
||||
| Аспект | Требование |
|
||||
|-------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Новые env-переменные | **Нет** |
|
||||
| Новые секреты | **Нет** |
|
||||
| Новые API-ключи | **Нет** |
|
||||
| Изменения `config/gps_sources.yaml` | **Нет** |
|
||||
| Изменения `config/gps_regions.yaml` | **Нет** |
|
||||
| Изменения runtime config | **Нет** — `GPS_TRACKS_MIN_ZOOM` остаётся хардкодом в `src/web/gps_tracks.js` (BRD §3 Out of scope: «feature-flag для minzoom не вводим») |
|
||||
|
||||
## 6. Деплой
|
||||
|
||||
### 6.1 Среды
|
||||
|
||||
- **dev (локально)**: `make dev` (docker compose up `app` + `gps-collector` с overrides). Достаточно `git pull && make dev` для смены поведения.
|
||||
- **test (mva154)**: `https://openclaw.mva154.duckdns.org/enduro/`.
|
||||
CI/CD — Gitea Actions; деплой через `make deploy-test` или ручной
|
||||
SSH + `docker compose up -d --no-deps --build app` (см. §6.2).
|
||||
- **prod** — пока не задействован; ET-012 деплоится только в test.
|
||||
|
||||
### 6.2 Процедура деплоя в test
|
||||
|
||||
Последовательность шагов (REQ-F-19 в TRZ §3):
|
||||
|
||||
1. **Сборка образа**: `docker compose build app` на mva154 (после `git pull`).
|
||||
2. **Перезапуск `app`**: `docker compose up -d --no-deps app`.
|
||||
3. **Smoke-проверка nginx gzip**:
|
||||
```bash
|
||||
curl -sI -H 'Accept-Encoding: gzip' \
|
||||
'https://openclaw.mva154.duckdns.org/enduro/api/gps-tracks/tiles/5/19/9.mvt' \
|
||||
| grep -i 'content-encoding'
|
||||
```
|
||||
Ожидается `content-encoding: gzip`.
|
||||
4. **Очистка серверного MVT-кэша** (опционально, но рекомендуется
|
||||
после изменения tier-таблицы):
|
||||
```bash
|
||||
curl -sX POST 'http://app:8000/api/gps-tracks/cache/clear'
|
||||
```
|
||||
(Endpoint доступен только из docker-network, см. ADR-008 §7.)
|
||||
5. **Ручная валидация AC-03..AC-08, AC-09..AC-10** через DevTools.
|
||||
6. **Запись результатов в `13-test-report.md` и `14-deploy-log.md`** (REQ-F-19).
|
||||
|
||||
### 6.3 Rollback
|
||||
|
||||
В случае проблем (например, размер MVT z5 > 200 KB на реальных данных
|
||||
→ деградация мобильного клиента):
|
||||
|
||||
1. **Backend rollback**: `git revert <commit>` + `docker compose up -d --no-deps --build app`.
|
||||
2. **Frontend rollback**: тот же образ; пользователи получают старый
|
||||
`gps_tracks.js` при следующем reload.
|
||||
3. **Cache invalidation после rollback**: `POST /api/gps-tracks/cache/clear`.
|
||||
|
||||
RTO: ≤ 5 минут (один `docker compose up -d --no-deps app`).
|
||||
RPO: 0 — никаких изменений в БД, никаких данных не теряется.
|
||||
|
||||
### 6.4 CI/CD-гейты
|
||||
|
||||
- `make lint` (ruff + eslint) — должен быть зелёным (AC-21).
|
||||
- `make test` (pytest unit + integration) — зелёный (AC-11..AC-14, AC-21).
|
||||
- `pytest -m perf` (PERF-Z5-01) — отдельный джоб, **не блокирующий
|
||||
merge** в MVP, но логируется в `13-test-report.md`. Если при росте
|
||||
БД (например, после очередного `gps-collector` runс +500 треков)
|
||||
тест начинает фейлить — задача в backlog: ужесточить tier-лимиты
|
||||
или ввести pre-rendering (ADR-016 вариант P-B).
|
||||
|
||||
## 7. Observability / Логирование
|
||||
|
||||
| Аспект | Требование |
|
||||
|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Новые лог-сообщения | **Нет** (NFR-07 в TRZ §4) |
|
||||
| Существующие лог-сообщения | `uvicorn.access` логирует все запросы к `/api/gps-tracks/tiles/{z}/{x}/{y}.mvt` с длиной ответа — этого достаточно для мониторинга размера MVT z5 |
|
||||
| Метрики / Prometheus | Не вводим в MVP. Если в будущем понадобятся p95-метрики build_gps_mvt — отдельный work-item (DevOps) |
|
||||
| Health-endpoint | `GET /api/gps-tracks/health` — без изменений; возвращает состояние БД, число треков по источникам |
|
||||
|
||||
### 7.1 Что мониторить после деплоя
|
||||
|
||||
В `nginx access.log` на mva154 (вручную, без алёртов):
|
||||
|
||||
- **Размер ответа на `/tiles/5/*/*.mvt`**: средняя ≤ 80 KB (после gzip),
|
||||
максимум ≤ 200 KB. Если max превышает 200 KB — ужесточить tier
|
||||
(`limit=1000` вместо 1500 для z=5).
|
||||
- **Status codes**: только 200. Никаких 500/502 на z=5..7 (отлично
|
||||
индикатор регрессии).
|
||||
- **Latency p95**: ≤ 700 мс cold, ≤ 50 мс hit (M-7).
|
||||
|
||||
Эти проверки выполняются вручную в первую неделю после деплоя; если
|
||||
стабильно — закрываются.
|
||||
|
||||
## 8. Резервное копирование / Disaster recovery
|
||||
|
||||
| Аспект | Требование |
|
||||
|------------------------------|-----------------------------------------------------------------------------------------------------|
|
||||
| Backup БД | Без изменений — БД `gps_tracks.sqlite` бэкапится тем же crontab-скриптом, что и раньше (ET-008) |
|
||||
| Время восстановления (RTO) | ≤ 5 минут (rollback контейнера, см. §6.3) |
|
||||
| Точка восстановления (RPO) | 0 — никаких данных не теряется |
|
||||
|
||||
## 9. Безопасность
|
||||
|
||||
| Аспект | Требование |
|
||||
|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
|
||||
| Auth / Authorization | Без изменений (NFR-05 в TRZ §4). Endpoint `/tiles/{z}/{x}/{y}.mvt` — публичный (как и был на z=8..11) |
|
||||
| Валидация входных данных | Без изменений; existing `0 ≤ z ≤ 22` в `get_gps_tile` уже корректно пропускает z=5..7 |
|
||||
| CSP | Без изменений |
|
||||
| Rate-limit | Не вводим в MVP (см. §3.2) |
|
||||
| TLS | Без изменений — nginx с Let's Encrypt сертификатом DuckDNS |
|
||||
|
||||
## 10. Совместимость
|
||||
|
||||
| Аспект | Требование |
|
||||
|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| API контракт `/api/gps-tracks/*` | Не меняется (REQ-F-15). Старые клиенты (старый `gps_tracks.js` со стороны браузера, который где-то закэшировался) продолжают запрашивать z=8..11 — endpoint отвечает корректно |
|
||||
| MapLibre GL JS совместимость | Без изменений; используем существующее `interpolate linear zoom` выражение, которое поддерживается всеми текущими версиями MapLibre |
|
||||
| Совместимость с `centralfederal.sqlite` | Не затронуто (это другая БД, для слоя `trails`) |
|
||||
| Совместимость с OSRM | Не затронуто (роутинг работает с OSRM-графом независимо) |
|
||||
| localStorage migration | Не нужно (REQ-F-18). Существующие ключи `gps-tracks-enabled`, `gps-tracks-activities`, `gps-tracks-sources`, `gps-tracks-color-mode` — без изменений |
|
||||
|
||||
## 11. Связанные документы
|
||||
|
||||
- `01-brd.md` §3 In/Out of scope, §6 Зависимости.Инфра
|
||||
- `02-trz.md` §3 REQ-F-19 Деплой и валидация, §4 NFR
|
||||
- `06-adr/ADR-016-z5-tiling-policy.md` §«Классификация изменения», §«Последствия»
|
||||
- `08-data-requirements.md` (этот пакет)
|
||||
- `10-tech-risks.md` (этот пакет)
|
||||
- `docs/work-items/ET-008/07-infra-requirements.md` §3 (nginx gzip для MVT, cache-clear network policy) — наследие
|
||||
- `docs/work-items/ET-011/07-infra-requirements.md` — образец «zero-infra» work-item
|
||||
270
docs/work-items/ET-012/08-data-requirements.md
Normal file
270
docs/work-items/ET-012/08-data-requirements.md
Normal file
@@ -0,0 +1,270 @@
|
||||
---
|
||||
type: data-requirements
|
||||
work_item_id: ET-012
|
||||
title: "Требования к данным — ET-012: Снижение minzoom публичных треков до z5"
|
||||
version: 1
|
||||
status: approved
|
||||
created_at: 2026-06-04
|
||||
authors:
|
||||
- "agent:architect"
|
||||
---
|
||||
|
||||
# Требования к данным — ET-012
|
||||
|
||||
## 1. Резюме
|
||||
|
||||
ET-012 — **pure read pattern change**. Никаких изменений схемы БД,
|
||||
никаких новых таблиц, индексов, миграций, файлов БД, ключей
|
||||
localStorage, изменений конфигов источников.
|
||||
|
||||
Меняется только **как** существующие данные читаются и
|
||||
сериализуются в MVT при `z ∈ {5, 6}`:
|
||||
|
||||
- `build_gps_mvt` отбирает другой набор `rows` (фильтр по `length_m`)
|
||||
и применяет более жёсткий лимит фич;
|
||||
- `_simplify_coords` применяет другой `tolerance` Douglas-Peucker'а
|
||||
к существующим WKB-координатам.
|
||||
|
||||
**Меняется:**
|
||||
- набор фич, попадающих в MVT-тайл при `z ∈ {5, 6}`;
|
||||
- размер итогового protobuf MVT (за счёт меньшего числа фич и более
|
||||
агрессивного упрощения).
|
||||
|
||||
**Не меняется:**
|
||||
- schema таблицы `tracks` (ET-008 / ADR-005);
|
||||
- schema таблицы `pipeline_runs`;
|
||||
- индексы `idx_tracks_geom` (R-tree), `min_lon/max_lon/min_lat/max_lat`;
|
||||
- контракт API `/api/gps-tracks/*` (REQ-F-15);
|
||||
- содержимое отдельных треков (geom, name, sources_json, etc.);
|
||||
- dedup-алгоритм (`compute_dedup_key`);
|
||||
- ACTIVITY_TYPES enum;
|
||||
- маппинги `SOURCE_ATTRIBUTIONS`, `SOURCE_LABELS`;
|
||||
- localStorage ключи и значения клиента (REQ-F-18);
|
||||
- содержимое `config/gps_sources.yaml`, `config/gps_regions.yaml`
|
||||
(REQ-F-16).
|
||||
|
||||
## 2. Архитектурные границы данных
|
||||
|
||||
| Слой данных | Тип | Расположение | Изменения в ET-012 |
|
||||
|-----------------------------------|----------------|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| OSM-vector (`trails`) | существующий | `/app/data/centralfederal.sqlite` | **нет** |
|
||||
| Личные GPX треки (ET-006) | существующий | браузер (memory) | **нет** |
|
||||
| Публичные GPS треки (ET-008) | существующий | `/app/data/gps_tracks.sqlite` | **read-only**: новые комбинации параметров `(z, x, y)` теперь принимаются (z=5/6/7); никаких INSERT/UPDATE/DELETE |
|
||||
| OSRM-граф | существующий | `/app/data/enduro.osrm.*` | **нет** |
|
||||
| User UI state | существующий | `localStorage` | **нет** новых ключей, нет миграции |
|
||||
| MVT-кэш в RAM `app` | существующий | `_gps_tile_cache` (Python dict) | **расширяется ключевым пространством**: теперь могут лежать тайлы с `z ∈ {5,6,7}` в дополнение к 8..11. Ёмкость 1024 — без изменений |
|
||||
| Серверный MVT-тайл (выход) | **существующий формат, новый z** | bytes в HTTP response | формат `application/x-protobuf` (Mapbox Vector Tile spec), source-layer `gps_tracks`, properties как в ET-008 (`id, activity, source, sources, length_km, name, ext_url`) |
|
||||
| Клиентский MapLibre LRU | существующий | браузер | **расширяется ключевым пространством** аналогично серверу |
|
||||
|
||||
## 3. Серверные данные — `gps_tracks.sqlite`
|
||||
|
||||
### 3.1 Schema
|
||||
|
||||
**Без изменений vs ET-008/ET-009/ET-011.** См.
|
||||
`docs/work-items/ET-008/08-data-requirements.md` §3.1, §3.5. Никаких
|
||||
ALTER TABLE / DROP COLUMN / CREATE INDEX.
|
||||
|
||||
### 3.2 Используемые поля в SELECT при сборке MVT z5-z7
|
||||
|
||||
| Поле | Использование |
|
||||
|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | MVT property |
|
||||
| `name` | MVT property |
|
||||
| `activity_type` | MVT property |
|
||||
| `length_m` | **NEW USE**: фильтр `length_m >= min_length_m` где `min_length_m=10000` (z5) или `5000` (z6) или `2000` (z7). Раньше фильтр применялся только для z≤7 с порогом 2000 |
|
||||
| `points_count` | не используется в MVT (только в `/download`, ET-011) |
|
||||
| `geom` (WKB) | парсится через `_wkb_to_coords()` → `[(lon, lat), ...]` → передаётся в `_simplify_coords(coords, z)`. **NEW**: для z=5 tolerance=0.04°, для z=6 tolerance=0.018° |
|
||||
| `sources_json` | первый элемент → MVT property `source`; весь список → comma-separated в property `sources` |
|
||||
| `external_urls_json` | первый URL → MVT property `ext_url` |
|
||||
| `dedup_key`, `description`, `tags_json`, `user`, `inserted_at`, `updated_at`, `created_at`, `min_lon..max_lat` | не используется в MVT (часть полей нужна только в `/download` или GeoJSON-режиме z≥12) |
|
||||
|
||||
Запрос идентичен ET-008 (`get_tracks_in_bbox`):
|
||||
|
||||
```sql
|
||||
SELECT t.* FROM tracks t WHERE t.ROWID IN (
|
||||
SELECT pkid FROM idx_tracks_geom WHERE
|
||||
xmin <= ? AND xmax >= ? AND ymin <= ? AND ymax >= ?
|
||||
) ORDER BY length_m DESC
|
||||
```
|
||||
|
||||
**Изменения SQL: нет.** Фильтр по `length_m` — на Python-стороне в
|
||||
`build_gps_mvt`, чтобы не вводить новые SQL-параметры (TRZ §3 REQ-F-08).
|
||||
|
||||
### 3.3 Объёмы данных
|
||||
|
||||
| Метрика | Текущее (ET-009) | Прогноз через 12 мес. | Гейт ET-012 |
|
||||
|------------------------------------------|---------------------|----------------------|------------------------------------------------------------|
|
||||
| Число треков в `gps_tracks.sqlite` | ~500 (test) | ~5000 | M-6 (p95 build_gps_mvt z5 ≤ 500 мс на БД 5000) |
|
||||
| Длинных треков (≥ 10 км) | ~150-200 (ЦФО) | ~1500-2000 | M-8 (размер MVT z5 ≤ 200 KB) |
|
||||
| Точек на трек (среднее) | 2000-5000 | 2000-5000 | (Tolerance Douglas-Peucker отсечёт лишнее) |
|
||||
| Размер БД (на диске) | ~50 MB | ~500 MB | Disk-impact на mva154 — пренебрежимо |
|
||||
|
||||
При БД из 5000 треков и БД-индекс по bbox:
|
||||
|
||||
- Один z=5 тайл накрывает ~1250×1250 км по экватору, ~700×1250 на 55° с.ш.
|
||||
- В bbox z=5 над ЦФО попадает ≤ 100% длинных треков ЦФО = ~1500.
|
||||
- После Python-фильтра `length_m ≥ 10000` остаётся ~1500 длинных
|
||||
треков → ограничивается `limit=1500`.
|
||||
- После `_simplify_coords` (tolerance 0.04° → ~5-30 точек на трек) →
|
||||
средний размер фичи ≈ 200 байт → MVT ≈ 300 KB до gzip → ≈ 80 KB после.
|
||||
|
||||
### 3.4 Индексы
|
||||
|
||||
**Без изменений vs ET-008.** Существующий R-tree-индекс
|
||||
`idx_tracks_geom` достаточен для bbox-запросов z=5. Вторичный индекс
|
||||
на `length_m` **не нужен** — `ORDER BY length_m DESC` дёшев на
|
||||
выборках < 5000 строк (Python sort после SQL-фильтра по bbox; SQLite
|
||||
делает табличный SCAN после R-tree фильтра).
|
||||
|
||||
**Watch-flag (TRZ §6, R-4):** если PERF-Z5-01 покажет деградацию при
|
||||
росте БД > 20k треков — рассмотреть `CREATE INDEX idx_tracks_length
|
||||
ON tracks(length_m DESC)` как отдельный work-item. Не в MVP.
|
||||
|
||||
## 4. Клиентские данные
|
||||
|
||||
### 4.1 localStorage
|
||||
|
||||
**Без изменений vs ET-008/ET-009/ET-011.** Используются ключи:
|
||||
|
||||
| Ключ | Назначение | Изменения в ET-012 |
|
||||
|----------------------------|---------------------------------------------|--------------------|
|
||||
| `gps-tracks-enabled` | bool — чекбокс «Публичные треки» | **нет** |
|
||||
| `gps-tracks-activities` | JSON-array — выбранные активности | **нет** |
|
||||
| `gps-tracks-sources` | JSON-array — выбранные источники | **нет** |
|
||||
| `gps-tracks-color-mode` | `"source" | "activity"` | **нет** |
|
||||
|
||||
REQ-F-18 в TRZ §3: «никакой миграции localStorage не нужно».
|
||||
Существующие сессии при следующей загрузке автоматически получают
|
||||
новый порог `GPS_TRACKS_MIN_ZOOM = 5` и видят слой на z5-z7.
|
||||
|
||||
### 4.2 MapLibre LRU (browser-side)
|
||||
|
||||
Браузерный MapLibre кэширует тайлы в собственном LRU. После ET-012:
|
||||
|
||||
- Ключевое пространство кэша: `(source_id, z, x, y)` — расширяется на
|
||||
`z ∈ {5, 6, 7}`.
|
||||
- Объём — управляется MapLibre, по умолчанию ~100 МБ; пользовательский
|
||||
опыт не страдает.
|
||||
- Никакой синхронизации с серверным `_gps_tile_cache` не нужно
|
||||
(independent caches; их инвалидация — через `POST /api/gps-tracks/cache/clear`,
|
||||
которая инвалидирует только серверный LRU; клиент дёрнет свежий MVT
|
||||
при следующем reload или после move-выхода-возврата за пределы LRU).
|
||||
|
||||
## 5. Контракты API
|
||||
|
||||
### 5.1 `GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt`
|
||||
|
||||
| Аспект | До ET-012 | После ET-012 |
|
||||
|-----------------------|--------------------------------------------------------|-------------------------------------------------------------------------------------|
|
||||
| Path-параметр `z` | принимается `0 ≤ z ≤ 22` | принимается `0 ≤ z ≤ 22` (без изменений) |
|
||||
| Response 200 | для z=8..11 — непустой MVT; для z<8 — пустой MVT | для z=5..11 — непустой MVT (новые z=5/6/7); для z<5 — пустой MVT |
|
||||
| Response Content-Type | `application/x-protobuf` | `application/x-protobuf` (без изменений) |
|
||||
| Properties фич | `id, activity, source, sources, length_km, name, ext_url` | без изменений |
|
||||
| Cache-Control | `public, max-age=300` | без изменений |
|
||||
| Размер тела (z5) | (раньше не использовалось клиентом, был ~0-50 KB пустой) | ≤ 200 KB до gzip (M-8) |
|
||||
|
||||
**Старые клиенты** (старый `gps_tracks.js`, который никогда не
|
||||
запрашивал z=5..7) — продолжают работать. Никакого breaking change
|
||||
в контракте нет.
|
||||
|
||||
### 5.2 `GET /api/gps-tracks?bbox=...`
|
||||
|
||||
**Без изменений.** Этот endpoint обслуживает GeoJSON-режим z≥12, а
|
||||
ET-012 не трогает z≥12.
|
||||
|
||||
### 5.3 `POST /api/gps-tracks/cache/clear`
|
||||
|
||||
**Без изменений.** Инвалидирует серверный `_gps_tile_cache` целиком
|
||||
(все z). Pipeline `gps-collector` дёргает его после успешного прогона
|
||||
(ADR-007 §7). После ET-012 этот вызов очищает и тайлы z=5..7
|
||||
автоматически.
|
||||
|
||||
### 5.4 `GET /api/gps-tracks/{id}/download`
|
||||
|
||||
**Без изменений.** ET-011 endpoint, не зависит от zoom.
|
||||
|
||||
### 5.5 `GET /api/gps-tracks/health`
|
||||
|
||||
**Без изменений.** Возвращает `tracks_total`, `tracks_by_source`,
|
||||
`last_pipeline_run`.
|
||||
|
||||
## 6. Миграции
|
||||
|
||||
**Нет.** Никаких миграций БД, никаких миграций localStorage,
|
||||
никаких миграций конфигов.
|
||||
|
||||
При деплое в test:
|
||||
|
||||
- БД `data/gps_tracks.sqlite` — без изменений (read-only для `app`).
|
||||
- `data/centralfederal.sqlite` — без изменений (другой слой).
|
||||
- Серверный MVT-кэш — очищается через `POST /api/gps-tracks/cache/clear`
|
||||
для подстраховки (см. `07-infra-requirements.md` §6.2 шаг 4); это
|
||||
не миграция, а кэш-инвалидация.
|
||||
- Клиентский MapLibre LRU — самоочищается при reload браузера; явной
|
||||
миграции не нужно.
|
||||
|
||||
## 7. Тестовые данные
|
||||
|
||||
### 7.1 Для unit-тестов
|
||||
|
||||
`tests/unit/test_gps_mvt_zoom_tiers.py` (новый, REQ-F-09):
|
||||
|
||||
- Использует in-memory SQLite (как существующие тесты в
|
||||
`tests/unit/test_gps_mvt.py`).
|
||||
- Фикстуры: треки разной длины (например, 1 км, 3 км, 6 км, 12 км,
|
||||
25 км), геометрия — простые LineString из 5-10 точек.
|
||||
- Никаких внешних зависимостей.
|
||||
|
||||
`tests/unit/test_gps_mvt_simplify.py` (новый или расширение, REQ-F-10):
|
||||
|
||||
- Чистые unit-тесты `_simplify_coords(coords, z)` — массивы coords
|
||||
захардкожены, БД не нужна.
|
||||
|
||||
### 7.2 Для integration-тестов
|
||||
|
||||
`tests/integration/test_gps_tile_z5_z7.py` (новый, REQ-F-11):
|
||||
|
||||
- Использует existing fixture `gps_tracks_test_db` (фикстура из
|
||||
`conftest.py` ET-008), которая заливает 50 треков по ЦФО разной
|
||||
длины с реалистичными координатами.
|
||||
- При необходимости расширяется до 200 треков для IT-Z5-02.
|
||||
|
||||
### 7.3 Для performance-теста
|
||||
|
||||
`tests/performance/test_gps_mvt_z5_perf.py` (новый, REQ-F-13):
|
||||
|
||||
- Fixture: 500 треков по ЦФО, каждый ≥ 10 км, реалистичная геометрия.
|
||||
- Маркер `@pytest.mark.perf` — не запускается в основном `make test`.
|
||||
- Запускается вручную или отдельным CI-джобом.
|
||||
|
||||
### 7.4 Для UI-тестов
|
||||
|
||||
`tests/e2e/test_ui_gps_z5.spec.ts` (новый, REQ-F-14 / `04b-ui-test-cases.md`):
|
||||
|
||||
- Запускается на test-среде `https://openclaw.mva154.duckdns.org/enduro/`.
|
||||
- Данные — реальная БД test-среды (после ET-009 — ~200 треков ЦФО).
|
||||
- Скриншот-эталоны для AC-08 (визуальная читаемость) — в
|
||||
`tests/e2e/screenshots/et012/`.
|
||||
|
||||
## 8. Резервные копии и DR
|
||||
|
||||
Без изменений vs ET-008. БД `gps_tracks.sqlite` бэкапится тем же
|
||||
crontab-скриптом, что и раньше. RPO = 0 (ET-012 не трогает данные).
|
||||
|
||||
## 9. Privacy / Compliance
|
||||
|
||||
| Аспект | Требование |
|
||||
|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| PII в новых MVT | **Нет нового PII.** На z=5..7 в MVT-фичу попадают те же поля, что и на z=8..11: `id, activity, source, sources, length_km, name, ext_url`. Поле `user` (потенциальный PII) в MVT не попадает на любых z. Поле `name` может содержать имя автора — но это уже было разрешено ET-008/ADR-005 для всех z ≥ 8. |
|
||||
| Licensing | **Без изменений** (ADR-009 OSM ODbL, ADR-010 EnduroRussia accepted, ADR-012 Wikiloc accepted с обезличиванием). Снижение minzoom не меняет, какие источники exposed клиенту — все треки в БД уже прошли licensing-guard pipeline'а |
|
||||
| Attribution | `MapLibre attribution control` отображает атрибуцию всех активных источников; это работает независимо от zoom — на z=5 пользователь видит те же бейджи «© OSM | EnduroRussia | © Wikiloc», что и на z=10 |
|
||||
|
||||
## 10. Связанные документы
|
||||
|
||||
- `01-brd.md` §6 Зависимости.Backend, §6 Зависимости.Тесты
|
||||
- `02-trz.md` §3 REQ-F-09..F-14 (тесты), REQ-F-16..F-18 (не меняем конфиги/стили/localStorage)
|
||||
- `06-adr/ADR-016-z5-tiling-policy.md` §«Решение», §«Последствия»
|
||||
- `07-infra-requirements.md` §4 (LRU, RAM), §6 (cache clear at deploy)
|
||||
- `10-tech-risks.md` (этот пакет)
|
||||
- `docs/work-items/ET-008/08-data-requirements.md` §3 (schema, индексы) — наследие
|
||||
- `docs/work-items/ET-009/08-data-requirements.md` (если есть) — наследие
|
||||
315
docs/work-items/ET-012/10-tech-risks.md
Normal file
315
docs/work-items/ET-012/10-tech-risks.md
Normal file
@@ -0,0 +1,315 @@
|
||||
---
|
||||
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 (контракты)
|
||||
Reference in New Issue
Block a user