mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-16 20:47:55 +00:00
This commit captures the full server code accumulated across
several development sessions. It includes the following layers,
applied in roughly this order during development:
1. Base infrastructure
- cmd/poc-server HTTP+WebSocket server, /health, /stats
- internal/transport wire types (Envelope + all message types)
- internal/catalog, internal/realtime, internal/stats
- internal/storage/postgres with migrations 001-004
- .env, .env.example, docker-compose, Dockerfile, scripts/
2. Swagger documentation
- go get github.com/swaggo/http-swagger
- swag init produces docs/docs.go
- /swagger/index.html UI, /swagger/doc.json spec
- annotations on health/stats/ws and catalog handlers
3. Races API
- internal/races/{store,service,keyset,types}.go
- migration 005_races.sql: finished_races, race_plans, race_queue
- GET /api/races with keyset pagination, status filter
- GET /api/races/upcoming
- POST /api/races/queue/join, GET, DELETE
- POST/GET /api/races/plans, DELETE /{id}
- lobby.RaceMeta simplification (no host_id/host_name)
4. Races seeder
- internal/races/seed with deterministic generator
- --seed-races / --reset CLI flags in main.go
- 30 finished + 5 live + 5 plans + 4 queue entries
5. Drivers and clans
- internal/drivers, internal/clans (CRUD, validation)
- migration 008_drivers_clans.sql
- /api/clans and /api/drivers endpoints
- 3-letter uppercase nickname/tag validation
- lobby.DriverMeta: Nickname, AvatarURL, ClanID, ClanTag
6. Podium
- internal/transport.RacePodiumEntry
- lobby.SetDriverProfile for in-memory metadata sync
- migration 007_podium.sql (podium JSONB column)
- seeder populates top-3 per finished race
7. Live races persistence
- migration 009_live_persistence.sql: live_races,
live_race_drivers, lobby_drivers
- internal/races/live_store.go: LiveStore with write-side
mirror for lobby.Service mutations
- Service.collectLive and Upcoming read from Postgres
- main.go RestoreFromDB rehydrates lobby on startup
8. Unify live + finished into one races table
- migration 010_unify_races.sql: rename finished_races to
races, expand status CHECK, merge live rows
- PgStore now hosts both write paths; LiveStore is a
thin facade implementing lobby.Persistence
- seeder resetAll drops only finished/cancelled rows and
race_plans / race_queue / lobby_drivers
Each layer is consistent on its own; cross-layer changes are
visible in the file history. A future refactor may split this
commit into the per-stage boundaries listed above.
503 lines
25 KiB
Markdown
503 lines
25 KiB
Markdown
# x0gp — Server Plan (Go)
|
||
|
||
Документ фиксирует серверную архитектуру: транспорт, модули, протокол,
|
||
хранение, биллинг, деплой.
|
||
|
||
Дата фиксации архитектурных решений: 2026-06-18.
|
||
Дата обновления (домашний сервер для PoC и MVP): 2026-06-18.
|
||
Дата обновления v2 (Orange Pi 5 Pro + Coolify + статический IP): 2026-06-18.
|
||
|
||
Архитектурные выборы, на которых основан план:
|
||
1. Транспорт клиент-сервер: **WebSocket + бинарный protobuf**.
|
||
2. Управление машинками: **Wi-Fi, ESP32-S3 агент**.
|
||
3. Модель доверия: **authoritative server + client-side prediction**.
|
||
4. Позиционирование: **IR-магнитные секторы + overhead-CV (гибрид)**.
|
||
|
||
---
|
||
|
||
## 1. Цели и не-цели
|
||
|
||
### 1.1. Цели
|
||
|
||
- Round-trip "команда на экране -> отклик машинки" в худшем случае 200 мс,
|
||
в типичном 80-120 мс.
|
||
- Поддержка одновременно 50-100 активных заездов на 1 Edge-инстанс.
|
||
- Устойчивость к обрывам связи (клиент теряет сеть на 0.5-2 с без потери
|
||
состояния).
|
||
- Anti-cheat: защита от подмены телеметрии, авто-команд, грязного руления.
|
||
- Биллинг заездов и турниров (ЮKassa для РФ, Stripe для остального).
|
||
- Горизонтальное масштабирование (stateless API, sticky-сессии на WS).
|
||
|
||
### 1.2. Не-цели (для MVP)
|
||
|
||
- Физика шин (скорость не считаем, считаем положение с CV).
|
||
- Запись/воспроизведение VR-реплеев.
|
||
- Полноценный matchmaking с ELO (только базовый лобби).
|
||
- Мобильное приложение (только Web, мобилка во второй очереди).
|
||
|
||
---
|
||
|
||
## 2. Высокоуровневая архитектура
|
||
|
||
```
|
||
+----------------------+
|
||
| React (Web client) |
|
||
| React Native (mobile)|
|
||
+----------+-----------+
|
||
|
|
||
WebSocket (binary protobuf)
|
||
|
|
||
+----------v-----------+
|
||
| Edge / API Gateway |
|
||
| (Go, stateless) |
|
||
+----+------+-----+----+
|
||
| | |
|
||
+-------------+ | +-------------+
|
||
| | |
|
||
+------v------+ +--------v-------+ +------v------+
|
||
| Race Engine | | Realtime Hub | | REST API |
|
||
| (stateful) | | (WS fan-out) | | (chi/echo) |
|
||
+-------------+ +----------------+ +-------------+
|
||
| | |
|
||
+---------+---------+ |
|
||
| |
|
||
+--------v--------+ |
|
||
| Redis | pub/sub, state |
|
||
+-----------------+ |
|
||
| |
|
||
+--------v--------+ |
|
||
| Postgres / TS |<-------------------+
|
||
+-----------------+
|
||
```
|
||
|
||
**Edge / API Gateway** — единая точка входа, TLS, rate-limit, auth, маршрутизация.
|
||
**Race Engine** — authoritative state гонки, tick 60 Hz.
|
||
**Realtime Hub** — WebSocket-концентратор, fan-out игрокам, сериализация.
|
||
**REST API** — управление пользователями, лобби, платежами, история.
|
||
|
||
---
|
||
|
||
## 3. Транспорт
|
||
|
||
### 3.1. WebSocket (основной)
|
||
|
||
- Библиотека: **gorilla/websocket** или **nbio** (последняя — non-blocking,
|
||
лучше для тысяч соединений).
|
||
- Формат кадров: **бинарный protobuf** (для latency) + опционально JSON для
|
||
не-real-time сообщений (чат лобби).
|
||
- Subprotocol: `x0gp.v1`.
|
||
- Ping/pong: каждые 5 с, читаем RTT для диагностики.
|
||
|
||
### 3.2. Альтернатива на будущее
|
||
|
||
- WebRTC DataChannel — если потребуется суб-50 мс jitter.
|
||
- Собственный UDP+KCP — если появится конкурентная потребность.
|
||
- В плане не реализуем, но держим "port" в протоколе для миграции.
|
||
|
||
### 3.3. REST API
|
||
|
||
- HTTP/2, chi router.
|
||
- OpenAPI 3.1 спецификация, кодогенерация клиента.
|
||
- Авторизация: JWT (access 15 мин) + refresh token (30 дней).
|
||
- Idempotency-Key для платёжных эндпоинтов.
|
||
|
||
---
|
||
|
||
## 4. Структура Go-модулей
|
||
|
||
```
|
||
server/
|
||
cmd/
|
||
api-gateway/ # REST + WS, stateless
|
||
race-engine/ # stateful, authoritative tick
|
||
agent-linker/ # запускается в помещении (на gateway-ПК)
|
||
indexer/ # пишет телеметрию в Timescale
|
||
replay-recorder/ # (опц.) пишет replay в S3
|
||
internal/
|
||
auth/ # JWT, сессии, refresh, MFA
|
||
billing/ # ЮKassa/Stripe, биллинг сессий
|
||
race/ # сессии, круги, DNF, рестарты, секторы
|
||
realtime/ # WS hub, fan-out, per-client очереди
|
||
transport/ # protobuf-схемы, кодеки
|
||
control/ # приём команд, rate-limit, prediction snapshots
|
||
prediction/ # client-side prediction серверные корректировки
|
||
telemetry/ # агрегация, пересылка в TS
|
||
anti_cheat/ # эвристики: sanity check, повторы, geo
|
||
tournament/ # bracket, лэддер, регистрация
|
||
storage/
|
||
postgres/ # sqlx/pgx, миграции
|
||
redis/ # сессии, pub/sub, лобби
|
||
timescale/ # телеметрия
|
||
obs/ # логирование, метрики, трейсы
|
||
proto/
|
||
client_server.proto # команды, телеметрия для игрока
|
||
server_agent.proto # команды и состояние для машинки
|
||
api/
|
||
openapi.yaml
|
||
migrations/ # SQL
|
||
deploy/
|
||
docker-compose.yml # dev
|
||
helm/ # prod
|
||
scripts/
|
||
loadtest/ # 100 виртуальных гонщиков
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Протокол (protobuf)
|
||
|
||
### 5.1. Клиент -> Сервер
|
||
|
||
- `ClientHello { version, token, locale }`
|
||
- `JoinRace { raceId, carClass }`
|
||
- `LeaveRace {}`
|
||
- `InputState { seq, tsMs, steering (-1..1), throttle (0..1), brake (0..1), gear (-1..0..1), buttons }` (60-120 Hz)
|
||
- `ChatMessage { text }` (rate-limited)
|
||
- `Ping { tsMs }`
|
||
|
||
### 5.2. Сервер -> Клиент
|
||
|
||
- `ServerHello { sessionId, raceTick, config }`
|
||
- `RaceSnapshot { tick, tsMs, cars: [{id, x, y, heading, speed, lap, sector, lastLapMs}], leaderboard }` (30-60 Hz)
|
||
- `InputAck { seq, appliedAtMs }`
|
||
- `Correction { deltaSteering, deltaThrottle, reason }` (редко, при расхождении)
|
||
- `RaceEvent { type: START|LAP|SECTOR|DNF|END, payload }`
|
||
- `TournamentUpdate { ... }`
|
||
- `Pong { tsMs }`
|
||
|
||
### 5.3. Сервер -> Бортовой агент
|
||
|
||
- `AgentCommand { carId, steering, throttle, brake, gear, flags, expiresAtMs }` (60-120 Hz)
|
||
- `AgentConfig { maxSteeringRate, maxThrottleRate, rampLimits }`
|
||
|
||
### 5.4. Бортовой агент -> Сервер
|
||
|
||
- `AgentHello { carId, fwVersion, hwId, rssi }`
|
||
- `AgentTelemetry { carId, tsMs, steeringActual, throttleActual, rpm, voltage, current, tempEsc, tempMotor, imu: {ax, ay, az, gx, gy, gz}, irSensor: {front, rear}, batteryPct }` (10-30 Hz)
|
||
- `Heartbeat { tsMs, uptimeSec, errors }` (1 Hz)
|
||
- `AgentLog { level, msg }`
|
||
|
||
### 5.5. Gateway-агент -> Edge-сервер
|
||
|
||
- `TrackHello { trackId, hwId, publicKey, version }`
|
||
- `CarFrame { carId, tsMs, position: {x,y,z}, heading, velocity, confidence }` (из CV, 30-60 Hz)
|
||
- `SectorEvent { carId, sector, tsMs, lap }`
|
||
- `ReplayChunk { raceId, tsMs, jpegBytes }` (по запросу)
|
||
- `LocalCommand { carId, payload }` (forwarded from server)
|
||
|
||
---
|
||
|
||
## 6. Race Engine (authoritative tick)
|
||
|
||
### 6.1. Tick
|
||
|
||
- Фиксированный шаг: **16.67 мс (60 Hz)**.
|
||
- На каждом тике:
|
||
1. Принять все InputState от игроков за последние N мс (батч).
|
||
2. Применить к authoritative state машинок (с учётом ramp limits).
|
||
3. Сформировать команды бортовым агентам.
|
||
4. Отправить команды агенту в помещении.
|
||
5. Через ~33 мс получить телеметрию и CV-обновления.
|
||
6. Скорректировать state.
|
||
7. Сформировать RaceSnapshot для клиентов.
|
||
|
||
### 6.2. Client-side prediction
|
||
|
||
- Клиент хранит последний применённый state и локально симулирует физику
|
||
между snapshot-ами.
|
||
- При получении Snapshot клиент сверяет, если расхождение > порога — плавная
|
||
коррекция (lerp 100-200 мс), без "телепорта".
|
||
- Сервер шлёт `Correction` только если расхождение > критического.
|
||
|
||
### 6.3. Anti-cheat эвристики
|
||
|
||
- Sanity check входящих команд (steering/throttle в допустимом диапазоне,
|
||
скорость изменения не выше физического предела).
|
||
- Сравнение CV-позиции с заявленной (на сервере) — если расходятся > N метров,
|
||
пометка DQF.
|
||
- Повторы replay: после заезда сверяем логгированные команды с CV-треком.
|
||
- Device fingerprint + поведенческий профиль.
|
||
- Rate limit: > 200 Hz InputState = бан.
|
||
|
||
### 6.4. Состояния гонки
|
||
|
||
- `LOBBY` -> `COUNTDOWN` (3..1) -> `RACING` -> `FINISHED`.
|
||
- DNF по: таймаут без input 5 с, потеря связи с агентом > 3 с, флаг
|
||
администратора.
|
||
- RESTART: только на LoBBY или между раундами.
|
||
|
||
---
|
||
|
||
## 7. Realtime Hub
|
||
|
||
- Один WS-коннект на игрока.
|
||
- Подписки: `race:{raceId}`, `tournament:{id}`, `user:{id}`.
|
||
- Fan-out: при Snapshot race-движок публикует в Redis pub/sub, Hub читает
|
||
и шлёт подписанным клиентам.
|
||
- Backpressure: если клиент не успевает читать — дропаем старые Snapshot,
|
||
шлём только последний.
|
||
- Sticky-сессии: cookie `x0gp_sticky` при балансировке на L7.
|
||
|
||
---
|
||
|
||
## 8. Хранение
|
||
|
||
### 8.1. PostgreSQL (relational)
|
||
|
||
Таблицы (основные):
|
||
- `users` (id, email, phone, password_hash, created_at, locale)
|
||
- `sessions` (id, user_id, refresh_hash, ip, ua, expires_at)
|
||
- `wallets` (user_id, balance, currency)
|
||
- `payments` (id, user_id, provider, ext_id, amount, status, created_at)
|
||
- `races` (id, track_id, class, started_at, ended_at, winner_id, replay_url)
|
||
- `race_results` (race_id, user_id, car_id, lap_times[], best_lap, dnf_reason)
|
||
- `tournaments` (id, name, format, entry_fee, prize_pool, status)
|
||
- `tournament_entries` (tournament_id, user_id, seed, eliminated_at)
|
||
- `cars` (id, hw_id, model, fw_version, status, last_seen)
|
||
- `tracks` (id, name, layout_id, geo_city, status)
|
||
|
||
Миграции: `golang-migrate/migrate`.
|
||
|
||
### 8.2. Redis
|
||
|
||
- `session:{token}` -> userId (TTL 15 мин)
|
||
- `refresh:{token}` -> userId (TTL 30 дн)
|
||
- `race:{id}:state` -> serialized race state (TTL 1 час)
|
||
- `lobby:{id}` -> set of userIds
|
||
- `ratelimit:{user}:{op}` -> token bucket
|
||
- pub/sub channel `race_events`
|
||
|
||
### 8.3. TimescaleDB (time-series)
|
||
|
||
- hypertable `telemetry` (ts, race_id, car_id, field, value)
|
||
- 7 дней горячих данных, потом cold в S3.
|
||
- continuous aggregates для "лучший круг на треке", "топ-скорости".
|
||
|
||
---
|
||
|
||
## 9. Биллинг
|
||
|
||
### 9.1. Провайдеры
|
||
|
||
- **ЮKassa** — для РФ.
|
||
- **Stripe** — для остального мира (фаза 3).
|
||
|
||
### 9.2. Сценарии
|
||
|
||
- Пополнение кошелька (one-time payment).
|
||
- Списание за заезд (раз в N минут или за сессию).
|
||
- Входной взнос в турнир.
|
||
- Призовые начисления победителям.
|
||
|
||
### 9.3. Идемпотентность и реконсиляция
|
||
|
||
- Каждый платёж имеет `Idempotency-Key` на стороне клиента и `ext_id` от
|
||
провайдера.
|
||
- Webhook-эндпоинт `/webhooks/yookassa` с проверкой подписи.
|
||
- Ежедневный job сверяет балансы с провайдером.
|
||
|
||
---
|
||
|
||
## 10. Авторизация
|
||
|
||
- Email + password (argon2id) — базовый сценарий.
|
||
- OAuth Yandex / VK / Google — для удобства.
|
||
- MFA TOTP — обязательно для админов и pro-игроков.
|
||
- JWT: HS256 в PoC, RS256 в проде.
|
||
- Refresh-token rotation, device-binding.
|
||
|
||
---
|
||
|
||
## 11. Anti-cheat
|
||
|
||
Уровни:
|
||
1. **Sanity** — диапазоны, скорости, ramp limits (всегда).
|
||
2. **CV-check** — расхождение с overhead-камерой > 1 м -> пометка.
|
||
3. **Replay verify** — после заезда можно пересчитать (асинхронно).
|
||
4. **Behavioral** — паттерны (слишком "идеальные" круги = подозрительно).
|
||
5. **Manual review** — оператор смотрит replay при споре.
|
||
|
||
---
|
||
|
||
## 12. Observability
|
||
|
||
- Логи: `slog` (stdlib) в JSON, отправка в Loki.
|
||
- Метрики: `prometheus/client_golang`, экспорт `/metrics`.
|
||
- Трейсы: OpenTelemetry, OTLP в Jaeger/Tempo.
|
||
- Ключевые метрики:
|
||
- `ws_connections_total`
|
||
- `rtt_ms_p50/p95/p99` (по `(clientSendTs - serverRecvTs)`)
|
||
- `tick_lag_ms` (если тик опаздывает > 5 мс — алерт).
|
||
- `input_rate_per_client`
|
||
- `dropped_snapshots_per_client`
|
||
- `gateway_online{trackId}` (heartbeat агенты в помещении).
|
||
|
||
---
|
||
|
||
## 13. Деплой и инфраструктура
|
||
|
||
**Базовое решение: PoC и MVP крутятся на Orange Pi 5 Pro через Coolify.**
|
||
Перенос на облако/VPS — когда появится коммерческая нагрузка и SLA обязательства.
|
||
|
||
### 13.1. Orange Pi 5 Pro (PoC и MVP)
|
||
|
||
**Платформа: Orange Pi 5 Pro** (Rockchip RK3588S, ARM64). Уже есть в наличии.
|
||
|
||
**Характеристики:**
|
||
- CPU: 8 cores (4× Cortex-A76 @ 2.4 GHz + 4× Cortex-A55 @ 1.8 GHz)
|
||
- RAM: 4 / 8 / 16 / 32 GB LPDDR4x (зависит от версии)
|
||
- GPU: Mali-G610 MP4
|
||
- **NPU: 6 TOPS** (RKNN) — для CV-инференса (YOLO/ByteTrack) **на борту**
|
||
- Сеть: 2× 2.5 GbE (один WAN, один LAN с трассой — **физическая изоляция**)
|
||
- Wi-Fi 6 (резерв)
|
||
- USB 3.0 × 2, PCIe 2.1 x1, SATA
|
||
- ОС: Ubuntu (уже стоит, с **Coolify**)
|
||
|
||
**Coolify как платформа деплоя:**
|
||
- Self-hosted PaaS, уже развёрнут.
|
||
- UI для управления сервисами, доменами, SSL, env-vars.
|
||
- Push-to-deploy из Git-репо (GitHub/Gitea).
|
||
- Сервисы = Docker Compose под капотом, но с UI и без боли.
|
||
- Встроенные: PostgreSQL, Redis, MinIO (S3-совместимое), Plausible, Uptime Kuma.
|
||
- Версионирование деплоев, rollback в 1 клик.
|
||
|
||
**Доступ из интернета (статический IP уже есть):**
|
||
- **Основной вариант**: Caddy reverse proxy + Let's Encrypt напрямую.
|
||
Coolify умеет провизить LE-сертификаты из коробки.
|
||
- Подходит, т.к. провайдер даёт статический IP, 443/tcp открыт на роутере
|
||
через port-forward на Orange Pi.
|
||
- Cloudflare Tunnel не нужен (нет смысла добавлять лишний hop).
|
||
- WAF: Crowdsec через Coolify (опц., можно отложить).
|
||
|
||
**Архитектура:**
|
||
```
|
||
+---------------- ORANGE PI 5 PRO (Ubuntu + Coolify) ----------------+
|
||
| |
|
||
| Coolify (docker compose orchestration) |
|
||
| +-- api-gateway ------------------------------------+ |
|
||
| +-- race-engine ----------------------------------+ |
|
||
| +-- postgres+timescale (через Coolify UI) | |
|
||
| +-- redis -----------------------------------+ |
|
||
| +-- caddy (LE) -----------------------------------+ |
|
||
| +-- prometheus+grafana -----------------------------------+ |
|
||
| +-- minio (S3 replays) -----------------------------------+ |
|
||
| |
|
||
| Natively (systemd) - требуют прямой доступ к железу: |
|
||
| +-- agent-linker (Wi-Fi к машинкам) |
|
||
| +-- cv-tracker (USB камеры, NPU RKNN) |
|
||
| +-- node_exporter (мониторинг) |
|
||
| |
|
||
+---------------------------------------------------------------------+
|
||
2.5 GbE #1 (WAN, к роутеру) 2.5 GbE #2 (LAN к трассе)
|
||
| |
|
||
vv
|
||
+--------------+ +---------------------+
|
||
| Internet | | Switch (или точка- |
|
||
| (clients) | | точка) |
|
||
+--------------+ +----------+----------+
|
||
|
|
||
+------v------+
|
||
| x0gp-track |
|
||
| AP + камеры |
|
||
+-------------+
|
||
```
|
||
|
||
**Изоляция сети трассы:**
|
||
- LAN-порт Orange Pi → выделенный коммутатор (или напрямую к точке доступа
|
||
x0gp-track).
|
||
- В этой сети только машинки и камеры — никаких домашних устройств.
|
||
- AP настраивается на изолированный SSID / VLAN.
|
||
- Это даёт предсказуемый latency (нет соседского трафика) и безопасность.
|
||
|
||
**Использование NPU RK3588S:**
|
||
- RKNN Toolkit 2 для конвертации моделей (PyTorch → ONNX → RKNN).
|
||
- Поддерживаются YOLOv8/v9, ByteTrack через onnxruntime-rknn.
|
||
- Целевой инференс: 60-120 FPS на одной камере 1080p (с запасом).
|
||
- Если NPU не подходит — fallback на CPU ARM A76.
|
||
- Альтернативный inference-сервер: Ultralytics + onnxruntime-rknn.
|
||
|
||
**ARM64-специфика:**
|
||
- Docker-образы должны быть `linux/arm64` или `multi-arch`.
|
||
- Postgres+TimescaleDB, Redis, Caddy, Prometheus, Grafana — есть для arm64.
|
||
- Go-сервисы — `GOARCH=arm64` при сборке (по умолчанию если собирать на
|
||
Orange Pi).
|
||
- CV-инференс на Python — pin версии onnxruntime-rknn под aarch64.
|
||
|
||
**Бэкапы:**
|
||
- Ежедневный `pg_dump` (cron в Coolify или отдельный scheduled task).
|
||
- Backup в MinIO (S3-совместимое) внутри Coolify.
|
||
- Опционально off-site: rclone → Backblaze B2 / Yandex Object Storage.
|
||
- Retention: 14 дней локально, 30 дней off-site.
|
||
|
||
**Безопасность:**
|
||
- SSH: только ключи, fail2ban.
|
||
- Coolify UI: за reverse proxy с basic auth или Cloudflare Access.
|
||
- Порты наружу: 80/tcp + 443/tcp (для Caddy/LE).
|
||
- Postgres и Redis слушают только 127.0.0.1 внутри Coolify.
|
||
- UPS для Orange Pi (microSD карты не любят внезапные отключения).
|
||
|
||
### 13.2. Production (после MVP, когда появится коммерческая нагрузка)
|
||
|
||
- VPS в Москве и СПб (2 региона) — Selectel / Yandex Cloud / Timeweb.
|
||
- Managed Postgres + Redis (для снятия с Orange Pi задач бэкапа и HA).
|
||
- Kubernetes (Yandex Cloud / Selectel / AWS) — HPA по метрикам
|
||
ws_connections, tick_lag.
|
||
- Redis Cluster, Postgres с read-replica.
|
||
- CDN для статики (CloudFront / Cloudflare).
|
||
- WAF (CrowdSec).
|
||
- Orange Pi остаётся как **gateway-агент + CV-инференс** в помещении с
|
||
трассой (NPU продолжаем использовать для позиционирования).
|
||
- Edge-сервер (Go) переезжает на VPS, к которому подключаются несколько
|
||
Orange Pi из разных помещений.
|
||
|
||
---
|
||
|
||
## 14. Производительность и SLO
|
||
|
||
| Метрика | Цель |
|
||
|---|---|
|
||
| API p99 latency | < 100 мс |
|
||
| WS input->ack | < 30 мс на Edge |
|
||
| Tick stability | 60 Hz +/- 2 мс |
|
||
| Один race-engine | 100 активных гонок |
|
||
| Один api-gateway | 5000 WS-клиентов |
|
||
| Доступность | 99.5% (PoC), 99.9% (prod) |
|
||
|
||
---
|
||
|
||
## 15. Roadmap (server)
|
||
|
||
| Фаза | Срок | Что делаем |
|
||
|---|---|---|
|
||
| **S0: PoC моно-сервис** | 1-2 нед | Один Go-бинарь: WS + REST + race state in-memory + локальный agent-linker. SQLite. Без биллинга. |
|
||
| **S1: MVP модульный** | 4-6 нед | Разделение на api-gateway / race-engine / agent-linker. Postgres + Redis. Auth, JWT, лобби. |
|
||
| **S2: CV + телеметрия** | 8-10 нед | Интеграция с overhead-CV. TimescaleDB. Replay recorder (S3-совместимое). |
|
||
| **S3: Биллинг** | 10-12 нед | ЮKassa, кошелёк, сессии под оплатой. Webhooks. Админка. |
|
||
| **S4: Турниры** | 14-18 нед | Bracket, лэддер, призовые. Анти-чит уровня 2-3. |
|
||
| **S5: Продакшн** | 4-6 мес | K8s, мульти-регион, SLO, on-call. Stripe для остального мира. Мобильное приложение. |
|
||
|
||
---
|
||
|
||
## 16. Открытые вопросы
|
||
|
||
- **Стек frontend**: React + Vite, какие библиотеки для WS (reconnect,
|
||
backoff)? Кандидаты: `reconnecting-websocket`, `socket.io-client` (overkill,
|
||
но быстро), свой тонкий клиент на `WebSocket` API.
|
||
- **Cloudflare Tunnel vs WireGuard vs статический IP** — что выберем для
|
||
доступа к домашнему серверу? Дефолт: Cloudflare Tunnel (проще, есть
|
||
free-tier, базовый WAF). Уточним после тестов latency.
|
||
- **Бэкапы Postgres на NAS** — есть ли дома NAS или использовать внешнее
|
||
S3-совместимое (Backblaze B2 / Yandex Object Storage) для off-site бэкапов?
|
||
- **CDN/streaming для replay** — Mux / Cloudflare Stream / своё? Out of scope
|
||
для MVP, но в плане держим "порт".
|
||
- **Интеграция со стриминговыми платформами (Twitch)** — out of scope для
|
||
MVP, но в плане держим "порт".
|
||
- **Язык CV-сервиса**: Go (gocv) или Python (быстрее итерация, ONNX-рантайм)?
|
||
Дефолт: Python на PoC для скорости итераций, Go для prod-инференса
|
||
(меньше ресурсов, проще деплой).
|
||
- **Конкретные характеристики домашнего сервера** (CPU/RAM/диск/ОС) — нужно
|
||
для оценки, сколько одновременных гонок он потянет. В PLAN.md пока заложен
|
||
разумный минимум (4 CPU / 16 GB / 500 GB SSD).
|
||
- **ЮKassa-аккаунт** — есть ли уже ИП/ООО для приёма платежей, или
|
||
регистрировать при переходе на биллинг?
|