Files
IoT/doc/thinking/readiness-assessment-2026-10-05.md
T

141 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Оценка готовности IoT к production (2026-10-05)
> Три взгляда на один репозиторий: анализ A, анализ B и моё мнение.
> Все три — только чтением кода и документации, без сборки и запуска тестов
> (правила репо: команды — только через SSH на ВМ; сборку/прогоны не выполняли).
---
## 1. Анализ A (первый) — итог 5/10
**Вердикт:** цельный MVP, но по коду не считать production-ready для
надёжной multi-tenant эксплуатации.
### Блокеры
- **Не проверяется принадлежность namespace пользователю.** Middleware
проверяет JWT, но не передаёт `sub` обработчикам для сравнения с namespace
из URL. CRUD и выдача пароля устройства берут namespace из запроса.
Валидный токен не ограничивает пользователя его tenant-ом:
`auth.go:20`, `router.go:54`, `devices.go:179`. `exp` фактически необязателен
(`auth.go:120`).
- **Путь потери уже принятых сообщений.** Flusher заменяет буфер на пустой
ДО записи в PG, при ошибке только логирует. Consumer при этом удаляет
сообщения из SQS после добавления в буфер: `iot_telemetry_store.go:129`,
`:135`, `consumer.go:81`.
- **Health зелёный при остановившемся pipeline.** Ошибки запуска bridge/consumer
только логируются; горутина завершается, а `/health` всегда отдаёт `ok`:
`main.go:106`, `router.go:74`.
- **Доставка допускает потери и дубли.** Переполненная RAM-очередь bridge
сбрасывает сообщения; исчерпание SQS-ретраев заканчивается drop. При
at-least-once нет идемпотентного ключа против дублей: `sender.go:70`, `:112`.
### Что хорошо
Компоненты сведены в понятный единый сервис; есть MQTT-reconnect с повторной
подпиской, SQS-ретраи, graceful shutdown, разделение хранения устройств и
телеметрии, широкий набор нагрузочных сценариев.
### Чего не хватает до уверенного релиза
- Автоматических Go-тестов (`*_test.go` не найдено) и CI.
- Согласованности доков с кодом: гайд деплоя — `iot-service:v0.1.2`, Makefile —
`v0.1.13`; loadtest README описывает уже исправленную блокировку
MQTT-обработчика и visibility 30с, код задаёт 120с:
`deployment-nubes-production.md:3`, `Makefile:4`, `README.md:85`, `config.go:114`.
---
## 2. Анализ B (второй) — итог ~60–65%
**Вердикт:** рабочий MVP / технический прототип, не продакшн-готовый сервис.
### Что хорошо
- Конвейер устройство → EMQX → мост → SQS → consumer → PG (per-tenant)
работает end-to-end; подтверждено HISTORY и нагрузочными прогонами.
- Прогоны: baseline 2390/2390 без потерь/дублей; ACL-нарушения отсекаются;
reconnect-storm прошёл чисто.
- Код небольшой (~2,4 тыс. строк в монолите), аккуратный: graceful shutdown,
асинхронный SQS-диспатчер с ретраями, batch-insert, backoff, constant-time
сравнение паролей.
- Документация подробная: деплой без kubectl, интеграция с SQS, лог ревью и
фиксов, 10 нагрузочных сценариев.
- Auth и ACL в EMQX работают напрямую через PostgreSQL, без зависимости от монолита.
### Что мешает назвать готовым
1. **Нет ни одного Go-теста и нет CI.** Только Python-нагрузочные скрипты;
в `.github/` одни правила.
2. **Изоляция тенантов слабая.** JWT проверяет только `sub` и `exp`; namespace
из URL не сверяется с токеном. Без `JWT_HMAC_SECRET` подпись не проверяется.
Любой «валидный» токен читает устройства и пароли любого namespace.
3. **Пароли устройств в БД открытым текстом** и отдаются через GET. EMQX —
`password_hash_algorithm = plain`.
4. **`AUTH_TEST_MODE=true`** (любой Bearer) был включён на проде для диагностики.
HISTORY §38 — «вернуть false»; закрытия не видно.
5. **`/internal/mqtt/auth` и `/internal/mqtt/acl`** открыты на публичном порту
без авторизации; дублируют PG-auth в EMQX и уже не нужны. ACL выдаёт права
моста любому клиенту с `clientid`, совпадающим с мостовым.
6. **Нерешённая платформенная проблема:** плавающий обрыв ответов >~15–64 КБ и
WebSocket-обрывы ~150с; стабильного воспроизведения нет, разбор с DevOps
идёт. В коде обходится пагинацией.
7. **Отложено:** whitelist тенантских БД (любой namespace создаёт базу), DLQ,
soak 24ч.
8. **Легаси не вычищено:** `cmd/iot-operator`, `controllers/`, `internal/api/`,
`cmd/mqtt-bridge`, `cmd/sqs-consumer`; в `go.mod` 12 строк k8s/controller-runtime;
бинарники в `bin/` и `ws-probe/ws-probe`. `LEGACY.md` обещает убрать после
переписывания хендлеров, а оно сделано.
9. **Дрейф конфигурации:** `cookie = "iot-emqx-cookie-mvp"` захардкожен в
`emqx.conf`; параметры EMQX задаются только через env в deck-UI.
10. **Нет TLS-верификации** к PG (`verify_none`). Нет rate limiting и метрик
(только `/health` и admin-статистика).
### Минимум для продакшна
- привязать namespace к JWT, сделать `JWT_HMAC_SECRET` обязательным;
- убедиться, что `AUTH_TEST_MODE` выключен;
- хешировать пароли устройств или не отдавать повторно;
- удалить `/internal/mqtt/*` и легаси;
- добавить тесты и CI;
- закрыть вопрос обрывов на платформе.
---
## 3. Моё мнение (перепроверено по коду)
Оба анализа компетентны и в главном совпадают. Расхождение 5/10 vs 60–65% —
не в фактах, а в весе, который они дают одним и тем же находкам. Спорные места
я перепроверил сам — оба правы по существу.
### Подтверждается кодом (не предположение)
1. **Изоляция тенантов слабая** — верно. `middleware/auth.go` проверяет JWT
только на `sub`+`exp`; `sub` не сверяется с `{namespace}` (claims не
передаются в обработчики, `router.go` не извлекает `sub`). `exp` необязателен
(`if claims.Exp > 0`), без `JWT_HMAC_SECRET` подпись не проверяется.
2. **Путь потери принятых сообщений** — верно. `processTelemetry` → `InsertTelemetry`
кладёт в in-memory буфер и возвращает nil, после чего consumer безусловно
удаляет сообщение из SQS. `flushAll` подменяет буфер на пустой ДО записи в PG
и при ошибке только логирует. Данные могут молча пропасть.
3. **`/health` зелёный при упавшем pipeline** — верно. `main.go` ошибки
bridge/consumer только логирует; горутина завершается, `healthHandler` всегда
отдаёт `ok`.
4. **Нет Go-тестов и CI** — верно. `*_test.go` отсутствует, `.github/` — правила.
5. **`AUTH_TEST_MODE` и пароли plaintext** — верно и задокументировано
(`password_hash_algorithm=plain`, `AUTH_TEST_MODE=true` для DevOps, §38).
6. **Дрейф доков** — верно: гайд `v0.1.2` vs Makefile `v0.1.13`; loadtest README
30с vs код 120с. Анализ A зафиксировал точнее.
### Где анализ B точнее/дополняет
- Явно оговорил, что сборку/тесты не запускал (ограничение правил — SSH).
- Глубже разобрал открытые `/internal/mqtt/*`, TLS `verify_none`, захардкоженный
`cookie`, невычищенное легаси и k8s-зависимости в `go.mod`.
### Где я бы поправил обоих
- **Проценты условны.** Оба дают один вывод «MVP/прототип, не production-ready»;
цифры — риторика, а не измерение.
- **Приоритет блокеров:** главные технические — потеря сообщений и tenant-граница.
Plaintext-пароли и unauthed `/internal/mqtt/*` — важные, но второго эшелона
(периметр платформы + EMQX уже на PG-auth напрямую снижают остроту).
### Итог
**Рабочий MVP, пригодный для демо/пилота, но не production-ready.** Главные
блокеры: слабая tenant-изоляция, гарантированная потеря сообщений при сбое
flush, «слепой» health, отсутствие Go-тестов/CI и не закрытая платформенная
проблема wss ~150с / MTU. Моя оценка — между A и B, ближе к
«функционально готов, эксплуатационно — нет».