103 lines
8.4 KiB
Markdown
103 lines
8.4 KiB
Markdown
# Журнал сессии — 2026-10-10 (IoT: чистка легаси → ветка master → ревью → фиксы)
|
||
|
||
**Рабочая папка:** /home/naeel/nubes/SQS_und_IoT/IoT
|
||
**Ветка:** `master` (переименована из `feature/replace-kafka-with-sqs`)
|
||
**Состояние на конец сессии:** 28 коммитов сверх `origin/master`, **не запушено** (пуш — только по команде).
|
||
|
||
---
|
||
|
||
## 1. Чистка легаси
|
||
|
||
Цель владельца: «старое ГАВНО не всплывало».
|
||
|
||
- 🗑 Удалён код-кластер старого IoT (не импортировался монолитом, не собирался):
|
||
`cmd/iot-operator/`, `cmd/mqtt-bridge/`, `controllers/`, `api/v1alpha1/`,
|
||
`internal/api/` (старый REST + UI-embeds admin/console). Коммит `f152f54`.
|
||
- `go mod tidy` убрал k8s-зависимости (`k8s.io/*`, `controller-runtime`) — −57 строк `go.mod`, −614 `go.sum`.
|
||
- 🗑 `bin/` со старыми бинарниками (operator/kafka-consumer/mqtt-bridge).
|
||
- `iot-admin.html` (живой `/iot-admin`): секции Kafka/K8s Pods → «Очередь shared-SQS» + «Рантайм»; удалён CSS `.pod-*/.badge-*/.dot-*/.lag-bar*`. Коммит `42f0d6a`.
|
||
- `.gitignore`: убраны сталые пути бинарников.
|
||
- `LEGACY.md` обновлён (помечено `УДАЛЁН 2026-10-10`).
|
||
|
||
Осталось на диске вне git: `iot-console.html` со **старыми доменами** `sless.kube5s.ru` / `iot.kube5s.ru` (см. §6, требует решения владельца — какой домен).
|
||
|
||
## 2. Ветка → master, коммит/пуш
|
||
|
||
- SQS-service: `main` → `master`; IoT: `feature/replace-kafka-with-sqs` → `master` (старая `master` перезаписана, её 3 коммита влиты merge'ом, конфликт только в `.gitignore`).
|
||
- Пуш в Gitea (`gitea.services.ngcloud.ru/Nail/{SQS-service,IoT}`): `origin/master` создан/обновлён. Force не использовался.
|
||
|
||
## 3. Правки кода по ревью Opus
|
||
|
||
### Блокеры Terraform (Этап 2)
|
||
- `632e551` MQTT-пароль устройства отдаётся один раз в 201 + `phase` пишется в БД (Create).
|
||
- `a6ef9f9` `created_at` → RFC3339 + перечитывание после Create.
|
||
- `7e3828f` `telemetry_topic` + нормализация `metadata` (без drift `{}`/null).
|
||
- `7ea4923` валидация `namespace/name/device_id` (`^[a-zA-Z0-9][a-zA-Z0-9-]*$`, запрет `_ + # /`).
|
||
|
||
### Потери данных (Этап 1)
|
||
- `046db68` 🔴1 сериализация flush (`flushMu`) — Flush дожидается in-flight записи.
|
||
- `e0cd1ca` 🔴2 построчный fallback (ядовитая строка не роняет весь батч).
|
||
- `006dbfa` 🔴2 семантическая валидация envelope (пустые поля → DLQ).
|
||
- `01d21f6` 🔴3 слив буфера bridge при shutdown + безопасное закрытие канала.
|
||
- `241c98a` 🔴4 ретраи SQS до shutdown + таймаут на попытку.
|
||
- `69ae2ff` 🔴6 не рвать автореконнект (ретрай подписки, без `Disconnect`).
|
||
- `530ee7b` 🔴7 ретрай резолва DLQ + не терять битые при недоступной DLQ.
|
||
- `47d9a04` 🔴5 ретрай GetQueueUrl на старте + выход ≠0 при сбое подсистемы.
|
||
|
||
## 4. PRO-ревью (моё) — находки и фиксы
|
||
|
||
Разобрал работу предыдущей модели, нашёл 3 🔴 + 🟡/🟢:
|
||
|
||
- `98e6def` **#1** `os.Exit` обходил `defer iotStore.Close()` → финальный flush терялся. Вынес логику в `run() int`, `main = os.Exit(run())`.
|
||
- `6e4a844` **#2** «не подтверждать битое при недоступной DLQ» = ядовитое зацикливается. Добавлено чтение `ApproximateReceiveCount` (после `maxReceiveCount=5` — дроп).
|
||
- `c1733c5` **#3** `/ready` = «горутина запущена», а не «подписан». Bridge сам управляет флагом через `onSubscribed`.
|
||
|
||
### Этап 4 (изоляция/JWT)
|
||
- `ea569da` JWT: обязательный `exp`, 401 вместо 403, guard тест-режима, JSON-ошибки.
|
||
- `2bea1de` `replaceDSNDatabase` (устранено смешение тенантов: ошибка для key=value DSN, корректный разбор `sslrootcert=/path`) + guard namespace (`safeNamespaceRe`, `ErrInvalidNamespace`).
|
||
|
||
### Этап 3 (жизненный цикл)
|
||
- `75c30e5` readiness consumer по SQS (`onReady`), ограничение `wg.Wait` (`waitWithTimeout`, 15с), идемпотентный `Close` (`sync.Once`).
|
||
|
||
### Этап 5 (мелочи)
|
||
- `f37f216` удалён мёртвый `mqtt_auth.go` + constant-time админ-токен.
|
||
- `fa98020` `decodeJSON` (MaxBytesReader 64КиБ + DisallowUnknownFields) + таймауты сервера.
|
||
- `d7cb850` `DeleteMessageBatch` (до 10 за вызов).
|
||
- `1510efe` пагинация: tiebreaker `ts DESC, id DESC` + потолок `limit` + точный лог.
|
||
- `11bb38e` `MaxReconnectInterval` 30с + Debug для health-проб/телеметрии.
|
||
|
||
### Terraform / безопасность
|
||
- `eacc740` PATCH обновляет `metadata`.
|
||
- `0a51d70` 409 различает конфликт `name` vs `device_id` (`ErrDeviceIDExists`).
|
||
- `c1adb26` **P0-3** bcrypt-хэш паролей устройств и бриджа + EMQX `name=bcrypt, salt_rounds=10` (ключ `salt_rounds` подтверждён по исходникам emqx/emqx; Go `DefaultCost=10`).
|
||
|
||
### Тесты / доки
|
||
- `46744ac` юнит-тесты: `validateIdentifier` (handler), `replaceDSNDatabase` + `safeNamespaceRe` (iotpg).
|
||
- `1600339` деплой-дока: пароль отдаётся один раз, в БД bcrypt.
|
||
|
||
## 5. Повторное PRO-ревью — открытые находки
|
||
|
||
- 🟠 **ReceivedAt — точность до секунды.** `bridge/handler.go` пишет `time.RFC3339`, `consumer/processor.go` парсит `time.RFC3339`; субсекунды теряются. Очередь standard (не FIFO) → два показания в одну секунду могут встать не в том порядке; tiebreaker `id DESC` не чинит (id = порядок SQS-доставки). **Фикс: RFC3339Nano на обеих сторонах.**
|
||
- 🟡 Пустое тело (`msg.Body == ""`) подтверждается без DLQ — для консистентности тоже в DLQ.
|
||
- 🟡 `processBackoff`-sleep внутри цикла батча задерживает подтверждение соседей → redelivery (спасают дедуп + фоновый flusher). Пре-существующий дизайн.
|
||
- ⚠️ Валидация `namespace`/`exp` зависят от формата `sub`/токена платформы — не проверено.
|
||
- ⚠️ `flushMu` — глобальная сериализация (осознанный компромисс 🔴1 vs throughput).
|
||
- ⚠️ Гонки не прогнаны под `go test -race`.
|
||
|
||
## 6. Осталось (не сделано)
|
||
|
||
**Хвосты доделаны 2026-10-11:** `18cdea7` RFC3339Nano; `250bbdf` пустое тело → DLQ;
|
||
`49422ef` домены консоли (apiBase → origin, mqttBase → exqx…); `d195629` race-тест dispatcher.
|
||
|
||
Осталось:
|
||
- Проверить формат `sub` платформы (валидация namespace + JWT `exp`) — **проверка**, не код.
|
||
- Тесты на `flushAll`/`Close` (требуют мока PG/SQS; `go test -race` прогнан, dispatcher покрыт).
|
||
- Terraform-провайдер (следующая большая задача после IoT).
|
||
- SQS-хвосты (`CreateQueue`→`QueueNameExists`, `ListQueues`, JWT по `sub`, лимит по байтам).
|
||
|
||
## Правила (напомню себе)
|
||
- Коммитить после каждой правки; пуш — только по команде.
|
||
- Документировать всё, включая ошибки (HISTORY/ + doc/thinking/).
|
||
- Не задавать вопросов, ответ на которые есть в коде/доках.
|
||
- «Рекомендация без замера — не рекомендация».
|