Files
IoT/HISTORY/2026-10-10-session-log.md

103 lines
8.4 KiB
Markdown
Raw Permalink 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.
# Журнал сессии — 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/).
- Не задавать вопросов, ответ на которые есть в коде/доках.
- «Рекомендация без замера — не рекомендация».