---
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
