Фикс MONGO_URI, новые имена consumer/dashboard, HISTORY-доки, схема
This commit is contained in:
@@ -0,0 +1,99 @@
|
|||||||
|
# 10 — Code Review (Sonnet) + Fix
|
||||||
|
|
||||||
|
**Дата:** 2026-07-22
|
||||||
|
**Источник:** Анализ Sonnet (Claude) через VS Code Copilot Chat
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
### apps — исходный код приложений
|
||||||
|
|
||||||
|
| Папка | Роль | Зависимости |
|
||||||
|
|---|---|---|
|
||||||
|
| `iot-producer` | Генерирует события → RabbitMQ | `amqplib`, `express` |
|
||||||
|
| `iot-consumer` | RabbitMQ → Redis + MongoDB | `amqplib`, `redis`, `mongodb`, `express` |
|
||||||
|
| `iot-dashboard` | Redis → UI (Chart.js) | `express`, `redis` |
|
||||||
|
| `iot-demo` | Только HISTORY — не приложение | — |
|
||||||
|
|
||||||
|
### IOT_RMQ_DEMO — Terraform-конфиг стенда
|
||||||
|
|
||||||
|
6 ресурсов в 5 файлах: infra.tf (RabbitMQ + Redis + MongoDB), producer.tf, consumer.tf, dashboard.tf, locals.tf (все параметры вынесены сюда).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что хорошо
|
||||||
|
|
||||||
|
**Архитектура:**
|
||||||
|
- Чёткое разделение ответственности: каждое приложение делает одно дело
|
||||||
|
- Dashboard читает **только Redis** — правильно, никакой нагрузки на MongoDB
|
||||||
|
- `ch.prefetch(10)` у consumer — нормальное управление backpressure
|
||||||
|
- TTL-индекс в MongoDB (`expireAfterSeconds: 604800`, 7 дней) — события автоматически удалятся
|
||||||
|
- `zRemRangeByRank("iot:recent", 0, -1001)` — лента событий ограничена 1000 записями
|
||||||
|
|
||||||
|
**Terraform:**
|
||||||
|
- `adopt_existing_on_create = true` — удобно при повторных `apply` без пересоздания
|
||||||
|
- `depends_on` расставлены корректно: consumer ждёт все три сервиса, dashboard — только Redis
|
||||||
|
- Весь хардкод вынесен в locals.tf, в ресурсах чисто
|
||||||
|
- `sensitive = true` на `api_token`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблемы (на момент анализа)
|
||||||
|
|
||||||
|
### 🔴 Баг: `MONGO_URI` без схемы `mongodb://` — **ИСПРАВЛЕНО 2026-07-22**
|
||||||
|
|
||||||
|
В consumer.tf строка формировалась так:
|
||||||
|
```hcl
|
||||||
|
MONGO_URI = "${local.cons_mgo_user}:${local.cons_mgo_pass}@${local.cons_mgo_host}:27017/iot?authSource=admin"
|
||||||
|
```
|
||||||
|
Результат: `admin:@hostname:27017/iot?authSource=admin`
|
||||||
|
|
||||||
|
В consumer.js `MongoClient` получал этот URI и падал — схема `mongodb://` отсутствовала. Дефолтный fallback `mongodb://localhost:27017/iot` не срабатывал, потому что переменная окружения была задана (просто невалидна).
|
||||||
|
|
||||||
|
**Фикс (2026-07-22):**
|
||||||
|
```hcl
|
||||||
|
MONGO_URI = "mongodb://${local.cons_mgo_user}:${local.cons_mgo_pass}@${local.cons_mgo_host}:27017/iot?authSource=admin"
|
||||||
|
```
|
||||||
|
Результат: `mongodb://admin:@hostname:27017/iot?authSource=admin`
|
||||||
|
|
||||||
|
**Верификация:** consumer `errors=0` после фикса.
|
||||||
|
|
||||||
|
### 🟡 nack с requeue=true — потенциальный infinite loop
|
||||||
|
|
||||||
|
В consumer.js:
|
||||||
|
```js
|
||||||
|
ch.nack(msg, false, true); // requeue = true
|
||||||
|
```
|
||||||
|
При систематической ошибке (например MongoDB недоступна) сообщение будет бесконечно возвращаться в очередь и перечитываться.
|
||||||
|
|
||||||
|
**Рекомендация:** `requeue=false` + логировать потерянное сообщение.
|
||||||
|
|
||||||
|
### 🟡 `s3_name` — объявлена, но не используется
|
||||||
|
|
||||||
|
В main.tf есть переменная `s3_name`, которая нигде в TF-файлах стенда не применяется. Legacy от шаблона.
|
||||||
|
|
||||||
|
### 🟡 `.trigger` — пустой файл в `iot-producer`
|
||||||
|
|
||||||
|
Файл .trigger пустой. Если нужен для force-redeploy — добавить комментарий.
|
||||||
|
|
||||||
|
### 🟡 `requirements.txt` в Node.js-папках
|
||||||
|
|
||||||
|
Файлы `requirements.txt` остались от Flask-экспериментов в iot-producer и iot-consumer. Мусор.
|
||||||
|
|
||||||
|
### 🟡 MongoDB без пароля
|
||||||
|
|
||||||
|
`cons_mgo_pass = ""` в locals.tf. Для демо-стенда приемлемо, но зафиксировано как известное ограничение.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Статус на 2026-07-22
|
||||||
|
|
||||||
|
| Проблема | Статус |
|
||||||
|
|---|---|
|
||||||
|
| MONGO_URI без mongodb:// | ✅ Исправлено |
|
||||||
|
| nack + requeue=true | 🟡 Не исправлено (низкий приоритет) |
|
||||||
|
| s3_name не используется | 🟡 Не исправлено |
|
||||||
|
| .trigger пустой | 🟡 Не исправлено |
|
||||||
|
| requirements.txt мусор | 🟡 Не исправлено |
|
||||||
|
| MongoDB без пароля | ℹ️ Приемлемо для демо |
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# Prompt для Gemini: диаграмма пайплайна IoT Demo
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Создай красивую диаграмму (flowchart/архитектурную схему) пайплайна данных для IoT-демонстрации «Умный дом». Нужна **одна картинка** с 6 блоками сервисов и стрелками между ними.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сервисы (блоки)
|
||||||
|
|
||||||
|
### 1. Producer (верхний левый угол)
|
||||||
|
- **Название:** Producer
|
||||||
|
- **Подпись:** Node.js · Генератор IoT-событий
|
||||||
|
- **Форма:** прямоугольник, синий/голубой (#3B82F6 или похожий)
|
||||||
|
- **Иконка:** ⚙️ или 📡
|
||||||
|
- **Что делает:** Эмулирует 8 IoT-датчиков (температура °C, влажность %, энергопотребление kW), каждые 3 секунды генерирует случайное JSON-событие
|
||||||
|
|
||||||
|
### 2. RabbitMQ (центр-верх)
|
||||||
|
- **Название:** RabbitMQ
|
||||||
|
- **Подпись:** Очередь сообщений · iot-events
|
||||||
|
- **Форма:** прямоугольник, оранжевый (#FF6600 — брендовый цвет RabbitMQ)
|
||||||
|
- **Иконка:** 🐰 или 📬
|
||||||
|
- **Что делает:** Буфер сообщений AMQP. Принимает от Producer, отдаёт Consumer. Durable — не теряет при рестарте.
|
||||||
|
|
||||||
|
### 3. Consumer (правый верхний)
|
||||||
|
- **Название:** Consumer
|
||||||
|
- **Подпись:** Node.js · Обработчик
|
||||||
|
- **Форма:** прямоугольник, зелёный (#10B981)
|
||||||
|
- **Иконка:** 📥 или 🔄
|
||||||
|
- **Что делает:** Читает очередь RabbitMQ, параллельно пишет в Redis и MongoDB
|
||||||
|
|
||||||
|
### 4. Redis (левый-центр, под Producer)
|
||||||
|
- **Название:** Redis
|
||||||
|
- **Подпись:** Кэш · latest + counters + recent
|
||||||
|
- **Форма:** прямоугольник, красный (#DC2626 — брендовый цвет Redis)
|
||||||
|
- **Иконка:** ⚡ или 🗄️
|
||||||
|
- **Что делает:** Оперативный кэш для Dashboard. Хранит: последние значения датчиков (hash), счётчики по типам (hash), ленту 1000 событий (zset).
|
||||||
|
|
||||||
|
### 5. MongoDB (центр-низ)
|
||||||
|
- **Название:** MongoDB
|
||||||
|
- **Подпись:** Архив · TTL 7 дней
|
||||||
|
- **Форма:** прямоугольник, тёмно-зелёный (#00684A — брендовый цвет MongoDB)
|
||||||
|
- **Иконка:** 🍃 или 💾
|
||||||
|
- **Что делает:** Постоянный архив всех событий в коллекции iot_events. Автоудаление через 7 дней (TTL-индекс).
|
||||||
|
|
||||||
|
### 6. Dashboard (правый-низ)
|
||||||
|
- **Название:** Dashboard
|
||||||
|
- **Подпись:** Node.js + Chart.js · Веб-интерфейс
|
||||||
|
- **Форма:** прямоугольник, фиолетовый (#8B5CF6)
|
||||||
|
- **Иконка:** 📊 или 🖥️
|
||||||
|
- **Что делает:** Читает Redis, отдаёт JSON API и HTML с графиками. Bar chart (значения), Doughnut (счётчики), таблица событий. Автообновление каждые 3 сек.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Стрелки (потоки данных)
|
||||||
|
|
||||||
|
| От | К | Протокол | Подпись на стрелке |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Producer | RabbitMQ | AMQP (порт 5672) | JSON-события → очередь iot-events |
|
||||||
|
| RabbitMQ | Consumer | AMQP (порт 5672) | JSON-события ← очередь iot-events |
|
||||||
|
| Consumer | Redis (latest) | TCP (порт 6379) | HSET sensor_id → значение |
|
||||||
|
| Consumer | Redis (counters) | TCP (порт 6379) | HINCRBY device_type |
|
||||||
|
| Consumer | Redis (recent) | TCP (порт 6379) | ZADD лента событий |
|
||||||
|
| Consumer | MongoDB | TCP (порт 27017) | insertOne → коллекция iot_events |
|
||||||
|
| Redis | Dashboard | TCP (порт 6379) | HGETALL + ZRANGE (только чтение) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Расположение на схеме
|
||||||
|
|
||||||
|
```
|
||||||
|
Producer ──→ RabbitMQ ──→ Consumer
|
||||||
|
│
|
||||||
|
┌──────────────┼──────────────┐
|
||||||
|
↓ ↓ ↓
|
||||||
|
Redis MongoDB Redis
|
||||||
|
(latest, (архив) (counters,
|
||||||
|
recent) ...)
|
||||||
|
│ │ │
|
||||||
|
└──────────────┼──────────────┘
|
||||||
|
↓
|
||||||
|
Dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Стиль
|
||||||
|
|
||||||
|
- **Тёмный фон** (#0F172A или похожий) — как у самого дашборда
|
||||||
|
- **Блоки** с закруглёнными углами (border-radius 8–12px)
|
||||||
|
- **Цвета блоков** — брендовые цвета каждого сервиса (см. выше)
|
||||||
|
- **Текст в блоках** — белый или светлый
|
||||||
|
- **Стрелки** — с подписями протоколов (AMQP, TCP) и названиями данных
|
||||||
|
- **Иконки** в блоках приветствуются
|
||||||
|
- **Легенда** внизу: протоколы (AMQP — оранжевая стрелка, TCP — серая стрелка)
|
||||||
|
- Заголовок схемы: «IoT Demo — Умный дом» и подзаголовок «6 сервисов Nubes Cloud»
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Дополнительно
|
||||||
|
|
||||||
|
Можно добавить разделительную линию (или фон) показывающую, что все сервисы внутри Kubernetes-кластера, а наружу торчит только Dashboard (HTTP/HTTPS) и Producer/Consumer (для health-check).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Формат
|
||||||
|
|
||||||
|
Сгенерируй **SVG** или **PNG**, который можно вставить в README.md.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Промпт для Gemini — ФИНАЛЬНАЯ ПРАВКА
|
||||||
|
|
||||||
|
У тебя уже есть схема. В ней **две ошибки**, исправь только их:
|
||||||
|
|
||||||
|
## Ошибка 1: Dashboard соединён с MongoDB — УБРАТЬ
|
||||||
|
|
||||||
|
Никакой связи между Dashboard и MongoDB нет. Dashboard читает ТОЛЬКО Redis. Убери эту стрелку.
|
||||||
|
|
||||||
|
## Ошибка 2: Нет связи Consumer → MongoDB
|
||||||
|
|
||||||
|
Consumer пишет в MongoDB (insertOne). Добавь стрелку Consumer → MongoDB с подписью "TCP · insertOne · архив 7 дн".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Правильные связи (ВСЕ, ничего лишнего):
|
||||||
|
|
||||||
|
```
|
||||||
|
Producer ──AMQP──→ RabbitMQ ──AMQP──→ Consumer
|
||||||
|
│
|
||||||
|
┌───────────┼───────────┐
|
||||||
|
│ TCP │ TCP │ TCP
|
||||||
|
▼ ▼ ▼
|
||||||
|
Redis MongoDB Redis
|
||||||
|
(HSET/ZADD) (insertOne) (HINCRBY)
|
||||||
|
│
|
||||||
|
│ TCP (только чтение)
|
||||||
|
▼
|
||||||
|
Dashboard ──HTTP──→ Браузер
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что МЕНЯТЬ в имеющейся схеме:
|
||||||
|
|
||||||
|
1. **Удали** стрелку между Dashboard и MongoDB (в любую сторону)
|
||||||
|
2. **Добавь** стрелку Consumer → MongoDB (TCP, insertOne)
|
||||||
|
3. **Проверь** что Consumer → Redis (TCP, три команды: HSET, HINCRBY, ZADD)
|
||||||
|
4. **Проверь** что Redis → Dashboard (TCP, только чтение)
|
||||||
|
5. Всё. Больше ничего не трогай.
|
||||||
|
|
||||||
|
Никаких других изменений. Только эти две правки.
|
||||||
@@ -56,7 +56,7 @@ resource "nubes_nodejs" "consumer" {
|
|||||||
REDIS_HOST = local.cons_rds_host # хост Redis
|
REDIS_HOST = local.cons_rds_host # хост Redis
|
||||||
REDIS_PORT = "6379" # порт Redis
|
REDIS_PORT = "6379" # порт Redis
|
||||||
REDIS_PASS = local.cons_rds_pass # пароль Redis
|
REDIS_PASS = local.cons_rds_pass # пароль Redis
|
||||||
MONGO_URI = "${local.cons_mgo_user}:${local.cons_mgo_pass}@${local.cons_mgo_host}:27017/iot?authSource=admin"
|
MONGO_URI = "mongodb://${local.cons_mgo_user}:${local.cons_mgo_pass}@${local.cons_mgo_host}:27017/iot?authSource=admin"
|
||||||
MONGO_DB = "iot" # имя базы MongoDB
|
MONGO_DB = "iot" # имя базы MongoDB
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ locals {
|
|||||||
prod_timeout = "11m" # таймаут операций
|
prod_timeout = "11m" # таймаут операций
|
||||||
|
|
||||||
# --- Consumer (Node.js): RabbitMQ -> Redis + MongoDB ---
|
# --- Consumer (Node.js): RabbitMQ -> Redis + MongoDB ---
|
||||||
cons_resource_name = "iot-cons3" # имя инстанса (cons = CONSumer, версия 3)
|
cons_resource_name = "iot-cons4" # имя инстанса (cons = CONSumer, версия 4)
|
||||||
cons_domain = "iotcons22n" # поддомен -> iotcons22n.nodejsk8s.dev.nubes.ru
|
cons_domain = "iotcons23n" # поддомен -> iotcons23n.nodejsk8s.dev.nubes.ru
|
||||||
cons_git_path = "https://gitea.services.ngcloud.ru/Nail/tf-iot-consumer.git" # Git-репо с кодом consumer
|
cons_git_path = "https://gitea.services.ngcloud.ru/Nail/tf-iot-consumer.git" # Git-репо с кодом consumer
|
||||||
cons_git_revision = "03fefc0" # хеш коммита (менять для редеплоя)
|
cons_git_revision = "03fefc0" # хеш коммита (менять для редеплоя)
|
||||||
cons_cpu = 300 # CPU millicores
|
cons_cpu = 300 # CPU millicores
|
||||||
@@ -50,8 +50,8 @@ locals {
|
|||||||
cons_timeout = "11m" # таймаут операций
|
cons_timeout = "11m" # таймаут операций
|
||||||
|
|
||||||
# --- Dashboard (Node.js + Chart.js): Redis -> графики ---
|
# --- Dashboard (Node.js + Chart.js): Redis -> графики ---
|
||||||
dash_resource_name = "iot-dash" # имя инстанса (dash = DASHboard)
|
dash_resource_name = "iot-dash2" # имя инстанса (dash = DASHboard, версия 2)
|
||||||
dash_domain = "iotdash22" # поддомен -> iotdash22.nodejsk8s.dev.nubes.ru
|
dash_domain = "iotdash23" # поддомен -> iotdash23.nodejsk8s.dev.nubes.ru
|
||||||
dash_git_path = "https://gitea.services.ngcloud.ru/Nail/tf-iot-dashboard.git" # Git-репо с кодом dashboard
|
dash_git_path = "https://gitea.services.ngcloud.ru/Nail/tf-iot-dashboard.git" # Git-репо с кодом dashboard
|
||||||
dash_git_revision = "bc6f43d" # хеш коммита (менять для редеплоя)
|
dash_git_revision = "bc6f43d" # хеш коммита (менять для редеплоя)
|
||||||
dash_cpu = 300 # CPU millicores
|
dash_cpu = 300 # CPU millicores
|
||||||
|
|||||||
Reference in New Issue
Block a user