docs(HISTORY): журнал сессии 2026-10-10 — легаси, ветка master, ревью и фиксы

Полная трассировка: чистка легаси, переименование в master, правки по ревью Opus,
PRO-ревью (3 находки), Этап 3/4/5, P0-3 bcrypt, тесты, открытые находки (§5),
осталось (§6).
This commit is contained in:
Repinoid
2026-10-10 19:29:06 +03:00
parent 85bcf26220
commit 099900580b
+101
View File
@@ -0,0 +1,101 @@
# Журнал сессии — 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. Осталось (не сделано)
- 🟠 RFC3339Nano для `ReceivedAt` (находка §5).
- 🟡 пустое тело → DLQ.
- Решить домены в `iot-console.html` (старые `kube5s` → выбрать актуальные; как в `landing.html`: `iot.containerk8s.dev.nubes.ru` / `exqx.containerk8s.dev.nubes.ru`).
- `go test -race` + тесты на `flushAll`/`Close`.
- Проверить формат `sub` платформы (валидация namespace + JWT `exp`).
- Terraform-провайдер (следующая большая задача после IoT).
- SQS-хвосты (`CreateQueue`→`QueueNameExists`, `ListQueues`, JWT по `sub`, лимит по байтам).
## Правила (напомню себе)
- Коммитить после каждой правки; пуш — только по команде.
- Документировать всё, включая ошибки (HISTORY/ + doc/thinking/).
- Не задавать вопросов, ответ на которые есть в коде/доках.
- «Рекомендация без замера — не рекомендация».